Fixed apparent type of homomorphic mapped type with non-homomorphic instantiation (#56727)

This commit is contained in:
Mateusz Burzyński
2024-01-09 20:13:50 +01:00
committed by GitHub
parent 1e00399a38
commit 424b96475e
4 changed files with 252 additions and 5 deletions

View File

@@ -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;

View File

@@ -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))
},
],
},
});

View File

@@ -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
},
],
},
});

View File

@@ -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,
},
],
},
});