From 578556befe94f05c6d616db85aa8c3a165e3c062 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 7 Apr 2024 12:24:47 -0700 Subject: [PATCH 1/2] Merge dc966c25e3fe8dcabe581d95a7cf8c32c1d84582 into 10b5059a9501000747e447cefb752c69108b594c --- src/compiler/checker.ts | 18 ++++--- .../baselines/reference/deepComparisons.types | 9 ++++ .../homomorphicMappedTypeNesting.symbols | 51 +++++++++++++++++++ .../homomorphicMappedTypeNesting.types | 32 ++++++++++++ .../compiler/homomorphicMappedTypeNesting.ts | 16 ++++++ 5 files changed, 119 insertions(+), 7 deletions(-) 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/src/compiler/checker.ts b/src/compiler/checker.ts index 175caa9eef16e..2211487e0a359 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14552,16 +14552,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; diff --git a/tests/baselines/reference/deepComparisons.types b/tests/baselines/reference/deepComparisons.types index 9dc04636a3db3..56c557b403c89 100644 --- a/tests/baselines/reference/deepComparisons.types +++ b/tests/baselines/reference/deepComparisons.types @@ -1,5 +1,14 @@ //// [tests/cases/compiler/deepComparisons.ts] //// +<<<<<<< HEAD +======= +=== Performance Stats === +Assignability cache: 300 / 300 (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) + +>>>>>>> 326241cd08 (Merge dc966c25e3fe8dcabe581d95a7cf8c32c1d84582 into 10b5059a9501000747e447cefb752c69108b594c) === deepComparisons.ts === function f1() { >f1 : () => void 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..cd6e632f48245 --- /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 251bc46d78a6f4742ff7276c79ce225dd44a4b5e Mon Sep 17 00:00:00 2001 From: TypeScript Bot Date: Mon, 8 Apr 2024 18:13:41 +0000 Subject: [PATCH 2/2] Update baselines --- tests/baselines/reference/deepComparisons.types | 9 --------- .../reference/homomorphicMappedTypeNesting.types | 7 ------- 2 files changed, 16 deletions(-) diff --git a/tests/baselines/reference/deepComparisons.types b/tests/baselines/reference/deepComparisons.types index 56c557b403c89..9dc04636a3db3 100644 --- a/tests/baselines/reference/deepComparisons.types +++ b/tests/baselines/reference/deepComparisons.types @@ -1,14 +1,5 @@ //// [tests/cases/compiler/deepComparisons.ts] //// -<<<<<<< HEAD -======= -=== Performance Stats === -Assignability cache: 300 / 300 (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) - ->>>>>>> 326241cd08 (Merge dc966c25e3fe8dcabe581d95a7cf8c32c1d84582 into 10b5059a9501000747e447cefb752c69108b594c) === deepComparisons.ts === function f1() { >f1 : () => void diff --git a/tests/baselines/reference/homomorphicMappedTypeNesting.types b/tests/baselines/reference/homomorphicMappedTypeNesting.types index cd6e632f48245..82eb61f12bc6e 100644 --- a/tests/baselines/reference/homomorphicMappedTypeNesting.types +++ b/tests/baselines/reference/homomorphicMappedTypeNesting.types @@ -5,28 +5,21 @@ 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 -> : ^