From 8c4c985f1a843ddfa747e1aef567d3efea5c5419 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 6 Apr 2024 14:18:42 -0700 Subject: [PATCH 1/4] Rework getResolvedApparentTypeOfMappedType --- src/compiler/checker.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8b1477961ab2d..c93d14122a99b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14810,16 +14810,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type.resolvedApparentType || (type.resolvedApparentType = getResolvedApparentTypeOfMappedType(type)); } - function getResolvedApparentTypeOfMappedType(type: MappedType) { + function getResolvedApparentTypeOfMappedType(type: MappedType): Type { const target = (type.target ?? type) as MappedType; const typeVariable = getHomomorphicTypeVariable(target); if (typeVariable && !target.declaration.nameType) { - const constraint = getConstraintTypeFromMappedType(type); - if (constraint.flags & TypeFlags.Index) { - const baseConstraint = getBaseConstraintOfType((constraint as IndexType).type); - if (baseConstraint && everyType(baseConstraint, t => isArrayOrTupleType(t) || isArrayOrTupleOrIntersection(t))) { - return instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, type.mapper)); - } + // We have a homomorphic mapped type or an instantiation of a homomorphic mapped type, i.e. a type + // of the form { [P in keyof T]: X }. Obtain the modifiers type (the T of the keyof T), and if it is + // another generic mapped type, recursively obtain its apparent type. Otherwise, obtain its base + // constraint. Then, if every constituent of the base constraint is an array or tuple type, apply + // this mapped type to the base constraint. It is safe to recurse when the modifiers type is a + // mapped type because we protect again circular constraints in getTypeFromMappedTypeNode. + const modifiersType = getModifiersTypeFromMappedType(type); + const baseConstraint = isGenericMappedType(modifiersType) ? getApparentTypeOfMappedType(modifiersType) : getBaseConstraintOfType(modifiersType); + if (baseConstraint && everyType(baseConstraint, t => isArrayOrTupleType(t) || isArrayOrTupleOrIntersection(t))) { + return instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, type.mapper)); } } return type; From f42d43a9e5463bae2f88b8d0d89988fd3ce33317 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 6 Apr 2024 14:19:11 -0700 Subject: [PATCH 2/4] Accept new baselines --- tests/baselines/reference/deepComparisons.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/deepComparisons.types b/tests/baselines/reference/deepComparisons.types index d62ade196c0e4..6871e12efc484 100644 --- a/tests/baselines/reference/deepComparisons.types +++ b/tests/baselines/reference/deepComparisons.types @@ -2,7 +2,7 @@ === Performance Stats === Assignability cache: 300 / 300 (nearest 100) -Type Count: 2,000 / 2,100 (nearest 100) +Type Count: 2,000 / 2,000 (nearest 100) Instantiation count: 3,500 / 3,500 (nearest 500) Symbol count: 26,500 / 27,000 (nearest 500) From c47afa6fe4f9add644a5a1caeaf91e25391be601 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 6 Apr 2024 14:19:25 -0700 Subject: [PATCH 3/4] Add regression test --- .../homomorphicMappedTypeNesting.symbols | 51 +++++++++++++++++++ .../homomorphicMappedTypeNesting.types | 32 ++++++++++++ .../compiler/homomorphicMappedTypeNesting.ts | 16 ++++++ 3 files changed, 99 insertions(+) create mode 100644 tests/baselines/reference/homomorphicMappedTypeNesting.symbols create mode 100644 tests/baselines/reference/homomorphicMappedTypeNesting.types create mode 100644 tests/cases/compiler/homomorphicMappedTypeNesting.ts diff --git a/tests/baselines/reference/homomorphicMappedTypeNesting.symbols b/tests/baselines/reference/homomorphicMappedTypeNesting.symbols new file mode 100644 index 0000000000000..da939c18d6efb --- /dev/null +++ b/tests/baselines/reference/homomorphicMappedTypeNesting.symbols @@ -0,0 +1,51 @@ +//// [tests/cases/compiler/homomorphicMappedTypeNesting.ts] //// + +=== homomorphicMappedTypeNesting.ts === +// Repro from #58060 + +type Box = { v: T }; +>Box : Symbol(Box, Decl(homomorphicMappedTypeNesting.ts, 0, 0)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 2, 9)) +>v : Symbol(v, Decl(homomorphicMappedTypeNesting.ts, 2, 30)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 2, 9)) + +type Test = T +>Test : Symbol(Test, Decl(homomorphicMappedTypeNesting.ts, 2, 38)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 4, 10)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 4, 10)) + +type UnboxArray = { +>UnboxArray : Symbol(UnboxArray, Decl(homomorphicMappedTypeNesting.ts, 4, 33)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 6, 16)) + + [K in keyof T]: T[K] extends Box ? R : never; +>K : Symbol(K, Decl(homomorphicMappedTypeNesting.ts, 7, 5)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 6, 16)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 6, 16)) +>K : Symbol(K, Decl(homomorphicMappedTypeNesting.ts, 7, 5)) +>Box : Symbol(Box, Decl(homomorphicMappedTypeNesting.ts, 0, 0)) +>R : Symbol(R, Decl(homomorphicMappedTypeNesting.ts, 7, 42)) +>R : Symbol(R, Decl(homomorphicMappedTypeNesting.ts, 7, 42)) + +}; + +type Identity = { [K in keyof T]: T[K] }; +>Identity : Symbol(Identity, Decl(homomorphicMappedTypeNesting.ts, 8, 2)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 10, 14)) +>K : Symbol(K, Decl(homomorphicMappedTypeNesting.ts, 10, 22)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 10, 14)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 10, 14)) +>K : Symbol(K, Decl(homomorphicMappedTypeNesting.ts, 10, 22)) + +declare function fnBad>>(...args: T): Test>>; +>fnBad : Symbol(fnBad, Decl(homomorphicMappedTypeNesting.ts, 10, 44)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 12, 23)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Box : Symbol(Box, Decl(homomorphicMappedTypeNesting.ts, 0, 0)) +>args : Symbol(args, Decl(homomorphicMappedTypeNesting.ts, 12, 53)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 12, 23)) +>Test : Symbol(Test, Decl(homomorphicMappedTypeNesting.ts, 2, 38)) +>Identity : Symbol(Identity, Decl(homomorphicMappedTypeNesting.ts, 8, 2)) +>UnboxArray : Symbol(UnboxArray, Decl(homomorphicMappedTypeNesting.ts, 4, 33)) +>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 12, 23)) + diff --git a/tests/baselines/reference/homomorphicMappedTypeNesting.types b/tests/baselines/reference/homomorphicMappedTypeNesting.types new file mode 100644 index 0000000000000..3b8b198ed7493 --- /dev/null +++ b/tests/baselines/reference/homomorphicMappedTypeNesting.types @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/homomorphicMappedTypeNesting.ts] //// + +=== homomorphicMappedTypeNesting.ts === +// Repro from #58060 + +type Box = { v: T }; +>Box : Box +> : ^^^^^^ +>v : T +> : ^ + +type Test = T +>Test : T +> : ^ + +type UnboxArray = { +>UnboxArray : UnboxArray +> : ^^^^^^^^^^^^^ + + [K in keyof T]: T[K] extends Box ? R : never; +}; + +type Identity = { [K in keyof T]: T[K] }; +>Identity : Identity +> : ^^^^^^^^^^^ + +declare function fnBad>>(...args: T): Test>>; +>fnBad : []>(...args: T) => Test>> +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ +>args : T +> : ^ + diff --git a/tests/cases/compiler/homomorphicMappedTypeNesting.ts b/tests/cases/compiler/homomorphicMappedTypeNesting.ts new file mode 100644 index 0000000000000..630c4d40cf51d --- /dev/null +++ b/tests/cases/compiler/homomorphicMappedTypeNesting.ts @@ -0,0 +1,16 @@ +// @strict: true +// @noEmit: true + +// Repro from #58060 + +type Box = { v: T }; + +type Test = T + +type UnboxArray = { + [K in keyof T]: T[K] extends Box ? R : never; +}; + +type Identity = { [K in keyof T]: T[K] }; + +declare function fnBad>>(...args: T): Test>>; From dc966c25e3fe8dcabe581d95a7cf8c32c1d84582 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 6 Apr 2024 15:05:28 -0700 Subject: [PATCH 4/4] Accept new baselines --- tests/baselines/reference/homomorphicMappedTypeNesting.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/homomorphicMappedTypeNesting.types b/tests/baselines/reference/homomorphicMappedTypeNesting.types index 3b8b198ed7493..cd6e632f48245 100644 --- a/tests/baselines/reference/homomorphicMappedTypeNesting.types +++ b/tests/baselines/reference/homomorphicMappedTypeNesting.types @@ -26,7 +26,7 @@ type Identity = { [K in keyof T]: T[K] }; declare function fnBad>>(...args: T): Test>>; >fnBad : []>(...args: T) => Test>> -> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^ >args : T > : ^