Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8427,16 +8427,9 @@ func (c *Checker) checkIndexedAccessIndexType(t *Type, accessNode *ast.Node) *Ty
// Check if the index type is assignable to 'keyof T' for the object type.
objectType := t.AsIndexedAccessType().objectType
indexType := t.AsIndexedAccessType().indexType
// skip index type deferral on remapping mapped types
var objectIndexType *Type
if c.isGenericMappedType(objectType) && c.getMappedTypeNameTypeKind(objectType) == MappedTypeNameTypeKindRemapping {
objectIndexType = c.getIndexTypeForMappedType(objectType, IndexFlagsNone)
} else {
objectIndexType = c.getIndexTypeEx(objectType, IndexFlagsNone)
}
hasNumberIndexInfo := c.getIndexInfoOfType(objectType, c.numberType) != nil
if everyType(indexType, func(t *Type) bool {
return c.isTypeAssignableTo(t, objectIndexType) || hasNumberIndexInfo && c.isApplicableIndexType(t, c.numberType)
return c.isTypeAssignableTo(t, c.getIndexTypeEx(objectType, IndexFlagsNone)) || hasNumberIndexInfo && c.isApplicableIndexType(t, c.numberType)
}) {
if accessNode.Kind == ast.KindElementAccessExpression && ast.IsAssignmentTarget(accessNode) && objectType.objectFlags&ObjectFlagsMapped != 0 && getMappedTypeModifiers(objectType)&MappedTypeModifiersIncludeReadonly != 0 {
c.error(accessNode, diagnostics.Index_signature_in_type_0_only_permits_reading, c.TypeToString(objectType))
Expand Down Expand Up @@ -28444,6 +28437,16 @@ func (c *Checker) getSimplifiedType(t *Type, writing bool) *Type {
return c.getSimplifiedIndexedAccessType(t, writing)
case t.flags&TypeFlagsConditional != 0:
return c.getSimplifiedConditionalType(t, writing)
case t.flags&TypeFlagsIndex != 0:
return c.getSimplifiedIndexType(t)
}
return t
}

func (c *Checker) getSimplifiedIndexType(t *Type) *Type {
target := t.AsIndexType().target
if c.isGenericMappedType(target) && c.getNameTypeFromMappedType(target) != nil && !c.isMappedTypeWithKeyofConstraintDeclaration(target) {
return c.getIndexTypeForMappedType(target, IndexFlagsNone)
}
return t
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//// [tests/cases/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts] ////

=== mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts ===
type ChildPaths<T> = [PathTuples<T>] extends [never] ? [] : PathTuples<T>;
>ChildPaths : Symbol(ChildPaths, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 16))
>PathTuples : Symbol(PathTuples, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 74))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 16))
>PathTuples : Symbol(PathTuples, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 74))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 16))

type PathTuples<T> = T extends object
>PathTuples : Symbol(PathTuples, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 74))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 1, 16))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 1, 16))

? {
[K in keyof T & string as K extends '_key' ? never : K]: [PathTuples<T[K]>] extends [never]
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 3, 7))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 1, 16))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 3, 7))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 3, 7))
>PathTuples : Symbol(PathTuples, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 74))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 1, 16))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 3, 7))

? never
: readonly [K, ...ChildPaths<T[K]>];
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 3, 7))
>ChildPaths : Symbol(ChildPaths, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 1, 16))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 3, 7))

}[Exclude<keyof T & string, '_key'>]
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 1, 16))

: [];

type T1 = PathTuples<{ a: string; _key: string; b: { c: number } }>;
>T1 : Symbol(T1, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 7, 7))
>PathTuples : Symbol(PathTuples, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 0, 74))
>a : Symbol(a, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 9, 22))
>_key : Symbol(_key, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 9, 33))
>b : Symbol(b, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 9, 47))
>c : Symbol(c, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 9, 52))

const t1: T1 = ['b', 'c'];
>t1 : Symbol(t1, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 10, 5))
>T1 : Symbol(T1, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 7, 7))

type S1<T> = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[Exclude<keyof T & string, '_key'>];
>S1 : Symbol(S1, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 10, 26))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 12, 8))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 12, 16))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 12, 8))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 12, 16))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 12, 16))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 12, 8))

type S2<T> = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[Exclude<string & keyof T, '_key'>];
>S2 : Symbol(S2, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 12, 111))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 13, 8))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 13, 16))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 13, 8))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 13, 16))
>K : Symbol(K, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 13, 16))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts, 13, 8))

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//// [tests/cases/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts] ////

=== mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts ===
type ChildPaths<T> = [PathTuples<T>] extends [never] ? [] : PathTuples<T>;
>ChildPaths : ChildPaths<T>

type PathTuples<T> = T extends object
>PathTuples : PathTuples<T>

? {
[K in keyof T & string as K extends '_key' ? never : K]: [PathTuples<T[K]>] extends [never]
? never
: readonly [K, ...ChildPaths<T[K]>];
}[Exclude<keyof T & string, '_key'>]
: [];

type T1 = PathTuples<{ a: string; _key: string; b: { c: number } }>;
>T1 : readonly ["a"] | readonly ["b", "c"]
>a : string
>_key : string
>b : { c: number; }
>c : number

const t1: T1 = ['b', 'c'];
>t1 : readonly ["a"] | readonly ["b", "c"]
>['b', 'c'] : ["b", "c"]
>'b' : "b"
>'c' : "c"

type S1<T> = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[Exclude<keyof T & string, '_key'>];
>S1 : S1<T>

type S2<T> = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[Exclude<string & keyof T, '_key'>];
>S2 : S2<T>

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ mappedTypeConstraints2.ts(16,11): error TS2322: Type 'Mapped3<K>[Uppercase<K>]'
Type 'Mapped3<K>[Uppercase<string>]' is not assignable to type '{ a: K; }'.
Type 'Mapped3<K>[string]' is not assignable to type '{ a: K; }'.
mappedTypeConstraints2.ts(42,7): error TS2322: Type 'Mapped6<K>[keyof Mapped6<K>]' is not assignable to type '`_${string}`'.
Type 'Mapped6<K>[`_${string}`]' is not assignable to type '`_${string}`'.
Type 'Mapped6<K>[`_${K}`]' is not assignable to type '`_${string}`'.
Type 'Mapped6<K>[`_${string}`]' is not assignable to type '`_${string}`'.
mappedTypeConstraints2.ts(59,57): error TS2322: Type 'Foo<T>[`get${T}`]' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Foo<T>[`get${T}`]'.
mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys<K>[`_${K}`]' is not assignable to type 'true'.
Expand Down Expand Up @@ -63,7 +64,8 @@ mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys<K
let s: `_${string}` = obj[key]; // Error
~
!!! error TS2322: Type 'Mapped6<K>[keyof Mapped6<K>]' is not assignable to type '`_${string}`'.
!!! error TS2322: Type 'Mapped6<K>[`_${string}`]' is not assignable to type '`_${string}`'.
!!! error TS2322: Type 'Mapped6<K>[`_${K}`]' is not assignable to type '`_${string}`'.
!!! error TS2322: Type 'Mapped6<K>[`_${string}`]' is not assignable to type '`_${string}`'.
}

type Mapped7<K extends string> = {
Expand Down Expand Up @@ -119,4 +121,8 @@ mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys<K
!!! error TS2322: Type 'ObjectWithUnderscoredKeys<K>[`_${K}`]' is not assignable to type 'true'.
!!! error TS2322: Type 'ObjectWithUnderscoredKeys<K>[`_${string}`]' is not assignable to type 'true'.
}

type M2<T> = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 };
function bare<T>(k: keyof M2<T>): 'x' | 'y' { return k; }
function inTemplate<T>(k: `p${keyof M2<T>}`): 'px' | 'py' { return k; }

Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ type ObjectWithUnderscoredKeys<K extends string> = {
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`]; // assignability fails here, but ideally should not
}

type M2<T> = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 };
function bare<T>(k: keyof M2<T>): 'x' | 'y' { return k; }
function inTemplate<T>(k: `p${keyof M2<T>}`): 'px' | 'py' { return k; }


//// [mappedTypeConstraints2.js]
Expand Down Expand Up @@ -132,6 +136,8 @@ function validate(obj, bounds) {
function genericTest(objectWithUnderscoredKeys, key) {
const shouldBeTrue = objectWithUnderscoredKeys[`_${key}`]; // assignability fails here, but ideally should not
}
function bare(k) { return k; }
function inTemplate(k) { return k; }


//// [mappedTypeConstraints2.d.ts]
Expand Down Expand Up @@ -185,3 +191,8 @@ type ObjectWithUnderscoredKeys<K extends string> = {
[k in K as `_${k}`]: true;
};
declare function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K): void;
type M2<T> = {
[K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1;
};
declare function bare<T>(k: keyof M2<T>): 'x' | 'y';
declare function inTemplate<T>(k: `p${keyof M2<T>}`): 'px' | 'py';
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,26 @@ function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnde
>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 88, 95))
}

type M2<T> = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 };
>M2 : Symbol(M2, Decl(mappedTypeConstraints2.ts, 90, 1))
>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 92, 8))
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 92, 16))
>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 92, 8))
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 92, 16))

function bare<T>(k: keyof M2<T>): 'x' | 'y' { return k; }
>bare : Symbol(bare, Decl(mappedTypeConstraints2.ts, 92, 77))
>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 93, 14))
>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 93, 17))
>M2 : Symbol(M2, Decl(mappedTypeConstraints2.ts, 90, 1))
>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 93, 14))
>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 93, 17))

function inTemplate<T>(k: `p${keyof M2<T>}`): 'px' | 'py' { return k; }
>inTemplate : Symbol(inTemplate, Decl(mappedTypeConstraints2.ts, 93, 57))
>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 94, 20))
>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 94, 23))
>M2 : Symbol(M2, Decl(mappedTypeConstraints2.ts, 90, 1))
>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 94, 20))
>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 94, 23))

Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,16 @@ function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnde
>key : K
}

type M2<T> = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 };
>M2 : M2<T>

function bare<T>(k: keyof M2<T>): 'x' | 'y' { return k; }
>bare : <T>(k: keyof M2<T>) => 'x' | 'y'
>k : keyof M2<T>
>k : "x" | "y"

function inTemplate<T>(k: `p${keyof M2<T>}`): 'px' | 'py' { return k; }
>inTemplate : <T>(k: `p${keyof M2<T>}`) => 'px' | 'py'
>k : `p${keyof M2<T>}`
>k : "px" | "py"

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// @strict: true
// @noEmit: true

type ChildPaths<T> = [PathTuples<T>] extends [never] ? [] : PathTuples<T>;
type PathTuples<T> = T extends object
? {
[K in keyof T & string as K extends '_key' ? never : K]: [PathTuples<T[K]>] extends [never]
? never
: readonly [K, ...ChildPaths<T[K]>];
}[Exclude<keyof T & string, '_key'>]
: [];

type T1 = PathTuples<{ a: string; _key: string; b: { c: number } }>;
const t1: T1 = ['b', 'c'];

type S1<T> = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[Exclude<keyof T & string, '_key'>];
type S2<T> = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[Exclude<string & keyof T, '_key'>];
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,7 @@ type ObjectWithUnderscoredKeys<K extends string> = {
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`]; // assignability fails here, but ideally should not
}

type M2<T> = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 };
function bare<T>(k: keyof M2<T>): 'x' | 'y' { return k; }
function inTemplate<T>(k: `p${keyof M2<T>}`): 'px' | 'py' { return k; }
Loading