Skip to content

Commit

Permalink
Fixed apparent type of homomorphic mapped type with non-homomorphic i…
Browse files Browse the repository at this point in the history
…nstantiation (#56727)
  • Loading branch information
Andarist authored Jan 9, 2024
1 parent 1e00399 commit 424b964
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 5 deletions.
28 changes: 23 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//// [tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts] ////

=== homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts ===
type HandleOptions<O> = {
>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<PropertyKey, readonly any[]>,
>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<T[K]>];
>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))

},
],
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//// [tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts] ////

=== homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts ===
type HandleOptions<O> = {
>HandleOptions : HandleOptions<O>

[I in keyof O]: {
value: O[I];
>value : O[I]

};
};

declare function func1<
>func1 : <T extends Record<PropertyKey, readonly any[]>>(fields: { [K in keyof T]: { label: string; options: [...HandleOptions<T[K]>]; }; }) => T

T extends Record<PropertyKey, readonly any[]>,
>(fields: {
>fields : { [K in keyof T]: { label: string; options: [...HandleOptions<T[K]>]; }; }

[K in keyof T]: {
label: string;
>label : string

options: [...HandleOptions<T[K]>];
>options : [...HandleOptions<T[K]>]

};
}): 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 : <T extends Record<PropertyKey, readonly any[]>>(fields: { [K in keyof T]: { label: string; options: [...HandleOptions<T[K]>]; }; }) => 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

},
],
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// @strict: true
// @noEmit: true

type HandleOptions<O> = {
[I in keyof O]: {
value: O[I];
};
};

declare function func1<
T extends Record<PropertyKey, readonly any[]>,
>(fields: {
[K in keyof T]: {
label: string;
options: [...HandleOptions<T[K]>];
};
}): T;

const result = func1({
prop: {
label: "first",
options: [
{
value: 123,
},
{
value: "foo",
},
],
},
other: {
label: "second",
options: [
{
value: "bar",
},
{
value: true,
},
],
},
});

0 comments on commit 424b964

Please sign in to comment.