diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index b03cebf741b22..d2759e02c2bf9 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -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)) @@ -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 } diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.symbols b/tsc/testdata/baselines/reference/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.symbols new file mode 100644 index 0000000000000..4084709731c93 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.symbols @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts] //// + +=== mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts === +type ChildPaths = [PathTuples] extends [never] ? [] : PathTuples; +>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 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] 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]; +>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] +>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 = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[Exclude]; +>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 = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[Exclude]; +>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)) + diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.types b/tsc/testdata/baselines/reference/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.types new file mode 100644 index 0000000000000..1aa96f787ee5f --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.types @@ -0,0 +1,35 @@ +//// [tests/cases/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts] //// + +=== mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts === +type ChildPaths = [PathTuples] extends [never] ? [] : PathTuples; +>ChildPaths : ChildPaths + +type PathTuples = T extends object +>PathTuples : PathTuples + + ? { + [K in keyof T & string as K extends '_key' ? never : K]: [PathTuples] extends [never] + ? never + : readonly [K, ...ChildPaths]; + }[Exclude] + : []; + +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 = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[Exclude]; +>S1 : S1 + +type S2 = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[Exclude]; +>S2 : S2 + diff --git a/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.errors.txt b/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.errors.txt index bc2f5287b0a48..5ddeb39a63aa5 100644 --- a/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.errors.txt +++ b/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.errors.txt @@ -4,7 +4,8 @@ mappedTypeConstraints2.ts(16,11): error TS2322: Type 'Mapped3[Uppercase]' Type 'Mapped3[Uppercase]' is not assignable to type '{ a: K; }'. Type 'Mapped3[string]' is not assignable to type '{ a: K; }'. mappedTypeConstraints2.ts(42,7): error TS2322: Type 'Mapped6[keyof Mapped6]' is not assignable to type '`_${string}`'. - Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'. + Type 'Mapped6[`_${K}`]' is not assignable to type '`_${string}`'. + Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'. mappedTypeConstraints2.ts(59,57): error TS2322: Type 'Foo[`get${T}`]' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'Foo[`get${T}`]'. mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys[`_${K}`]' is not assignable to type 'true'. @@ -63,7 +64,8 @@ mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys[keyof Mapped6]' is not assignable to type '`_${string}`'. -!!! error TS2322: Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'. +!!! error TS2322: Type 'Mapped6[`_${K}`]' is not assignable to type '`_${string}`'. +!!! error TS2322: Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'. } type Mapped7 = { @@ -119,4 +121,8 @@ mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys[`_${K}`]' is not assignable to type 'true'. !!! error TS2322: Type 'ObjectWithUnderscoredKeys[`_${string}`]' is not assignable to type 'true'. } + + type M2 = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 }; + function bare(k: keyof M2): 'x' | 'y' { return k; } + function inTemplate(k: `p${keyof M2}`): 'px' | 'py' { return k; } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.js b/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.js index 4c19254560680..0d6bbb9c12df4 100644 --- a/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.js +++ b/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.js @@ -92,6 +92,10 @@ type ObjectWithUnderscoredKeys = { function genericTest(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys, key: K) { const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`]; // assignability fails here, but ideally should not } + +type M2 = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 }; +function bare(k: keyof M2): 'x' | 'y' { return k; } +function inTemplate(k: `p${keyof M2}`): 'px' | 'py' { return k; } //// [mappedTypeConstraints2.js] @@ -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] @@ -185,3 +191,8 @@ type ObjectWithUnderscoredKeys = { [k in K as `_${k}`]: true; }; declare function genericTest(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys, key: K): void; +type M2 = { + [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1; +}; +declare function bare(k: keyof M2): 'x' | 'y'; +declare function inTemplate(k: `p${keyof M2}`): 'px' | 'py'; diff --git a/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.symbols b/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.symbols index 1cf513e4e63fc..5a0df70c026a2 100644 --- a/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.symbols +++ b/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.symbols @@ -316,3 +316,26 @@ function genericTest(objectWithUnderscoredKeys: ObjectWithUnde >key : Symbol(key, Decl(mappedTypeConstraints2.ts, 88, 95)) } +type M2 = { [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(k: keyof M2): '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(k: `p${keyof M2}`): '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)) + diff --git a/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.types b/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.types index f1da9e2f0e98e..a2ae31ae42b1c 100644 --- a/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.types +++ b/tsc/testdata/baselines/reference/conformance/mappedTypeConstraints2.types @@ -228,3 +228,16 @@ function genericTest(objectWithUnderscoredKeys: ObjectWithUnde >key : K } +type M2 = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 }; +>M2 : M2 + +function bare(k: keyof M2): 'x' | 'y' { return k; } +>bare : (k: keyof M2) => 'x' | 'y' +>k : keyof M2 +>k : "x" | "y" + +function inTemplate(k: `p${keyof M2}`): 'px' | 'py' { return k; } +>inTemplate : (k: `p${keyof M2}`) => 'px' | 'py' +>k : `p${keyof M2}` +>k : "px" | "py" + diff --git a/tsc/testdata/tests/cases/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts b/tsc/testdata/tests/cases/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts new file mode 100644 index 0000000000000..8c612c1d94cb9 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/mappedTypeAsClauseIntersectionConstraintIndexedAccess.ts @@ -0,0 +1,17 @@ +// @strict: true +// @noEmit: true + +type ChildPaths = [PathTuples] extends [never] ? [] : PathTuples; +type PathTuples = T extends object + ? { + [K in keyof T & string as K extends '_key' ? never : K]: [PathTuples] extends [never] + ? never + : readonly [K, ...ChildPaths]; + }[Exclude] + : []; + +type T1 = PathTuples<{ a: string; _key: string; b: { c: number } }>; +const t1: T1 = ['b', 'c']; + +type S1 = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[Exclude]; +type S2 = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[Exclude]; diff --git a/tsc/testdata/tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts b/tsc/testdata/tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts index 83738d21a5cc1..89a0fe20c52ae 100644 --- a/tsc/testdata/tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts +++ b/tsc/testdata/tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts @@ -93,3 +93,7 @@ type ObjectWithUnderscoredKeys = { function genericTest(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys, key: K) { const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`]; // assignability fails here, but ideally should not } + +type M2 = { [K in keyof T & string as [K] extends ['a'] ? 'x' : 'y']: 1 }; +function bare(k: keyof M2): 'x' | 'y' { return k; } +function inTemplate(k: `p${keyof M2}`): 'px' | 'py' { return k; }