mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-13 02:45:24 -05:00
Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//// [tests/cases/compiler/homomorphicMappedTypeNesting.ts] ////
|
||||
|
||||
=== homomorphicMappedTypeNesting.ts ===
|
||||
// Repro from #58060
|
||||
|
||||
type Box<T extends string> = { 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 extends string[]> = 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<T> = {
|
||||
>UnboxArray : Symbol(UnboxArray, Decl(homomorphicMappedTypeNesting.ts, 4, 33))
|
||||
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 6, 16))
|
||||
|
||||
[K in keyof T]: T[K] extends Box<infer R> ? 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<T> = { [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<T extends Array<Box<string>>>(...args: T): Test<Identity<UnboxArray<T>>>;
|
||||
>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))
|
||||
|
||||
25
tests/baselines/reference/homomorphicMappedTypeNesting.types
Normal file
25
tests/baselines/reference/homomorphicMappedTypeNesting.types
Normal file
@@ -0,0 +1,25 @@
|
||||
//// [tests/cases/compiler/homomorphicMappedTypeNesting.ts] ////
|
||||
|
||||
=== homomorphicMappedTypeNesting.ts ===
|
||||
// Repro from #58060
|
||||
|
||||
type Box<T extends string> = { v: T };
|
||||
>Box : Box<T>
|
||||
>v : T
|
||||
|
||||
type Test<T extends string[]> = T
|
||||
>Test : T
|
||||
|
||||
type UnboxArray<T> = {
|
||||
>UnboxArray : UnboxArray<T>
|
||||
|
||||
[K in keyof T]: T[K] extends Box<infer R> ? R : never;
|
||||
};
|
||||
|
||||
type Identity<T> = { [K in keyof T]: T[K] };
|
||||
>Identity : Identity<T>
|
||||
|
||||
declare function fnBad<T extends Array<Box<string>>>(...args: T): Test<Identity<UnboxArray<T>>>;
|
||||
>fnBad : <T extends Box<string>[]>(...args: T) => Test<Identity<UnboxArray<T>>>
|
||||
>args : T
|
||||
|
||||
16
tests/cases/compiler/homomorphicMappedTypeNesting.ts
Normal file
16
tests/cases/compiler/homomorphicMappedTypeNesting.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
// Repro from #58060
|
||||
|
||||
type Box<T extends string> = { v: T };
|
||||
|
||||
type Test<T extends string[]> = T
|
||||
|
||||
type UnboxArray<T> = {
|
||||
[K in keyof T]: T[K] extends Box<infer R> ? R : never;
|
||||
};
|
||||
|
||||
type Identity<T> = { [K in keyof T]: T[K] };
|
||||
|
||||
declare function fnBad<T extends Array<Box<string>>>(...args: T): Test<Identity<UnboxArray<T>>>;
|
||||
Reference in New Issue
Block a user