From 424b96475ec19964c3896b8057e0fa302b427b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 9 Jan 2024 20:13:50 +0100 Subject: [PATCH] Fixed apparent type of homomorphic mapped type with non-homomorphic instantiation (#56727) --- src/compiler/checker.ts | 28 +++++- ...omomorphicInstantiationSpreadable1.symbols | 92 ++++++++++++++++++ ...nHomomorphicInstantiationSpreadable1.types | 95 +++++++++++++++++++ ...hNonHomomorphicInstantiationSpreadable1.ts | 42 ++++++++ 4 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.symbols create mode 100644 tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.types create mode 100644 tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bccd6075b971a..a67de08bd5059 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2049,6 +2049,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var noConstraintType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); var circularConstraintType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); var resolvingDefaultType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); + var resolvingApparentMappedType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); var markerSuperType = createTypeParameter(); var markerSubType = createTypeParameter(); @@ -14459,15 +14460,32 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getApparentTypeOfMappedType(type: MappedType) { - return type.resolvedApparentType || (type.resolvedApparentType = getResolvedApparentTypeOfMappedType(type)); + if (type.resolvedApparentType) { + if (type.resolvedApparentType === resolvingApparentMappedType) { + return type.resolvedApparentType = type; + } + return type.resolvedApparentType; + } + type.resolvedApparentType = resolvingApparentMappedType; + return type.resolvedApparentType = getResolvedApparentTypeOfMappedType(type); } function getResolvedApparentTypeOfMappedType(type: MappedType) { - const typeVariable = getHomomorphicTypeVariable(type); - if (typeVariable && !type.declaration.nameType) { - const constraint = getConstraintOfTypeParameter(typeVariable); + const mappedType = type.target as MappedType || type; + const typeVariable = getHomomorphicTypeVariable(mappedType); + if (typeVariable && !mappedType.declaration.nameType) { + let constraint: Type | undefined; + if (!type.target) { + constraint = getConstraintOfTypeParameter(typeVariable); + } + else { + const modifiersConstraint = getConstraintOfType(getModifiersTypeFromMappedType(type)); + if (modifiersConstraint) { + constraint = getApparentType(modifiersConstraint); + } + } if (constraint && everyType(constraint, isArrayOrTupleType)) { - return instantiateType(type, prependTypeMapping(typeVariable, constraint, type.mapper)); + return instantiateType(mappedType, prependTypeMapping(typeVariable, constraint, mappedType.mapper)); } } return type; diff --git a/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.symbols b/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.symbols new file mode 100644 index 0000000000000..1d4d93ed814b4 --- /dev/null +++ b/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.symbols @@ -0,0 +1,92 @@ +//// [tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts] //// + +=== homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts === +type HandleOptions = { +>HandleOptions : Symbol(HandleOptions, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 0)) +>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19)) + + [I in keyof O]: { +>I : Symbol(I, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 3)) +>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19)) + + value: O[I]; +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 19)) +>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19)) +>I : Symbol(I, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 3)) + + }; +}; + +declare function func1< +>func1 : Symbol(func1, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 4, 2)) + + T extends Record, +>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) + +>(fields: { +>fields : Symbol(fields, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 8, 2)) + + [K in keyof T]: { +>K : Symbol(K, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 3)) +>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23)) + + label: string; +>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 19)) + + options: [...HandleOptions]; +>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 10, 18)) +>HandleOptions : Symbol(HandleOptions, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 0)) +>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23)) +>K : Symbol(K, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 3)) + + }; +}): T; +>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23)) + +const result = func1({ +>result : Symbol(result, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 15, 5)) +>func1 : Symbol(func1, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 4, 2)) + + prop: { +>prop : Symbol(prop, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 15, 22)) + + label: "first", +>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 16, 9)) + + options: [ +>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 17, 19)) + { + value: 123, +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 19, 7)) + + }, + { + value: "foo", +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 22, 7)) + + }, + ], + }, + other: { +>other : Symbol(other, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 26, 4)) + + label: "second", +>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 27, 10)) + + options: [ +>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 28, 20)) + { + value: "bar", +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 30, 7)) + + }, + { + value: true, +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 33, 7)) + + }, + ], + }, +}); diff --git a/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.types b/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.types new file mode 100644 index 0000000000000..19d882824d22f --- /dev/null +++ b/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.types @@ -0,0 +1,95 @@ +//// [tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts] //// + +=== homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts === +type HandleOptions = { +>HandleOptions : HandleOptions + + [I in keyof O]: { + value: O[I]; +>value : O[I] + + }; +}; + +declare function func1< +>func1 : >(fields: { [K in keyof T]: { label: string; options: [...HandleOptions]; }; }) => T + + T extends Record, +>(fields: { +>fields : { [K in keyof T]: { label: string; options: [...HandleOptions]; }; } + + [K in keyof T]: { + label: string; +>label : string + + options: [...HandleOptions]; +>options : [...HandleOptions] + + }; +}): T; + +const result = func1({ +>result : { prop: [number, string]; other: [string, boolean]; } +>func1({ prop: { label: "first", options: [ { value: 123, }, { value: "foo", }, ], }, other: { label: "second", options: [ { value: "bar", }, { value: true, }, ], },}) : { prop: [number, string]; other: [string, boolean]; } +>func1 : >(fields: { [K in keyof T]: { label: string; options: [...HandleOptions]; }; }) => T +>{ prop: { label: "first", options: [ { value: 123, }, { value: "foo", }, ], }, other: { label: "second", options: [ { value: "bar", }, { value: true, }, ], },} : { prop: { label: string; options: [{ value: number; }, { value: string; }]; }; other: { label: string; options: [{ value: string; }, { value: true; }]; }; } + + prop: { +>prop : { label: string; options: [{ value: number; }, { value: string; }]; } +>{ label: "first", options: [ { value: 123, }, { value: "foo", }, ], } : { label: string; options: [{ value: number; }, { value: string; }]; } + + label: "first", +>label : string +>"first" : "first" + + options: [ +>options : [{ value: number; }, { value: string; }] +>[ { value: 123, }, { value: "foo", }, ] : [{ value: number; }, { value: string; }] + { +>{ value: 123, } : { value: number; } + + value: 123, +>value : number +>123 : 123 + + }, + { +>{ value: "foo", } : { value: string; } + + value: "foo", +>value : string +>"foo" : "foo" + + }, + ], + }, + other: { +>other : { label: string; options: [{ value: string; }, { value: true; }]; } +>{ label: "second", options: [ { value: "bar", }, { value: true, }, ], } : { label: string; options: [{ value: string; }, { value: true; }]; } + + label: "second", +>label : string +>"second" : "second" + + options: [ +>options : [{ value: string; }, { value: true; }] +>[ { value: "bar", }, { value: true, }, ] : [{ value: string; }, { value: true; }] + { +>{ value: "bar", } : { value: string; } + + value: "bar", +>value : string +>"bar" : "bar" + + }, + { +>{ value: true, } : { value: true; } + + value: true, +>value : true +>true : true + + }, + ], + }, +}); diff --git a/tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts b/tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts new file mode 100644 index 0000000000000..e3e880dd4ae14 --- /dev/null +++ b/tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts @@ -0,0 +1,42 @@ +// @strict: true +// @noEmit: true + +type HandleOptions = { + [I in keyof O]: { + value: O[I]; + }; +}; + +declare function func1< + T extends Record, +>(fields: { + [K in keyof T]: { + label: string; + options: [...HandleOptions]; + }; +}): T; + +const result = func1({ + prop: { + label: "first", + options: [ + { + value: 123, + }, + { + value: "foo", + }, + ], + }, + other: { + label: "second", + options: [ + { + value: "bar", + }, + { + value: true, + }, + ], + }, +}); \ No newline at end of file