mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
Only call getLowerBoundOfKeyType on non-generic mapped types (#56280)
This commit is contained in:
parent
9302332481
commit
3e094edc97
@ -17493,30 +17493,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return constraintType;
|
||||
}
|
||||
const keyTypes: Type[] = [];
|
||||
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
|
||||
// We have a { [P in keyof T]: X }
|
||||
|
||||
// `getApparentType` on the T in a generic mapped type can trigger a circularity
|
||||
// (conditionals and `infer` types create a circular dependency in the constraint resolution)
|
||||
// so we only eagerly manifest the keys if the constraint is nongeneric
|
||||
if (!isGenericIndexType(constraintType)) {
|
||||
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
|
||||
forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlags.StringOrNumberLiteralOrUnique, !!(indexFlags & IndexFlags.StringsOnly), addMemberForKeyType);
|
||||
}
|
||||
else {
|
||||
// we have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer the whole `keyof whatever` for later
|
||||
// since it's not safe to resolve the shape of modifier type
|
||||
// Calling getApparentType on the `T` of a `keyof T` in the constraint type of a generic mapped type can
|
||||
// trigger a circularity. For example, `T extends { [P in keyof T & string as Captitalize<P>]: any }` is
|
||||
// a circular definition. For this reason, we only eagerly manifest the keys if the constraint is non-generic.
|
||||
if (isGenericIndexType(constraintType)) {
|
||||
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
|
||||
// We have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer
|
||||
// the whole `keyof whatever` for later since it's not safe to resolve the shape of modifier type.
|
||||
return getIndexTypeForGenericType(type, indexFlags);
|
||||
}
|
||||
// Include the generic component in the resulting type.
|
||||
forEachType(constraintType, addMemberForKeyType);
|
||||
}
|
||||
else if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
|
||||
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
|
||||
forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlags.StringOrNumberLiteralOrUnique, !!(indexFlags & IndexFlags.StringsOnly), addMemberForKeyType);
|
||||
}
|
||||
else {
|
||||
forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType);
|
||||
}
|
||||
if (isGenericIndexType(constraintType)) { // include the generic component in the resulting type
|
||||
forEachType(constraintType, addMemberForKeyType);
|
||||
}
|
||||
// we had to pick apart the constraintType to potentially map/filter it - compare the final resulting list with the original constraintType,
|
||||
// so we can return the union that preserves aliases/origin data if possible
|
||||
// We had to pick apart the constraintType to potentially map/filter it - compare the final resulting list with the
|
||||
// original constraintType, so we can return the union that preserves aliases/origin data if possible.
|
||||
const result = indexFlags & IndexFlags.NoIndexSignatures ? filterType(getUnionType(keyTypes), t => !(t.flags & (TypeFlags.Any | TypeFlags.String))) : getUnionType(keyTypes);
|
||||
if (result.flags & TypeFlags.Union && constraintType.flags & TypeFlags.Union && getTypeListId((result as UnionType).types) === getTypeListId((constraintType as UnionType).types)) {
|
||||
return constraintType;
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
//// [tests/cases/compiler/circularMappedTypeConstraint.ts] ////
|
||||
|
||||
=== circularMappedTypeConstraint.ts ===
|
||||
// Repro from #56232
|
||||
|
||||
declare function foo2<T extends { [P in keyof T & string as Capitalize<P>]: V }, V extends string>(a: T): T;
|
||||
>foo2 : Symbol(foo2, Decl(circularMappedTypeConstraint.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(circularMappedTypeConstraint.ts, 2, 22))
|
||||
>P : Symbol(P, Decl(circularMappedTypeConstraint.ts, 2, 35))
|
||||
>T : Symbol(T, Decl(circularMappedTypeConstraint.ts, 2, 22))
|
||||
>Capitalize : Symbol(Capitalize, Decl(lib.es5.d.ts, --, --))
|
||||
>P : Symbol(P, Decl(circularMappedTypeConstraint.ts, 2, 35))
|
||||
>V : Symbol(V, Decl(circularMappedTypeConstraint.ts, 2, 80))
|
||||
>V : Symbol(V, Decl(circularMappedTypeConstraint.ts, 2, 80))
|
||||
>a : Symbol(a, Decl(circularMappedTypeConstraint.ts, 2, 99))
|
||||
>T : Symbol(T, Decl(circularMappedTypeConstraint.ts, 2, 22))
|
||||
>T : Symbol(T, Decl(circularMappedTypeConstraint.ts, 2, 22))
|
||||
|
||||
export const r2 = foo2({A: "a"});
|
||||
>r2 : Symbol(r2, Decl(circularMappedTypeConstraint.ts, 3, 12))
|
||||
>foo2 : Symbol(foo2, Decl(circularMappedTypeConstraint.ts, 0, 0))
|
||||
>A : Symbol(A, Decl(circularMappedTypeConstraint.ts, 3, 24))
|
||||
|
||||
17
tests/baselines/reference/circularMappedTypeConstraint.types
Normal file
17
tests/baselines/reference/circularMappedTypeConstraint.types
Normal file
@ -0,0 +1,17 @@
|
||||
//// [tests/cases/compiler/circularMappedTypeConstraint.ts] ////
|
||||
|
||||
=== circularMappedTypeConstraint.ts ===
|
||||
// Repro from #56232
|
||||
|
||||
declare function foo2<T extends { [P in keyof T & string as Capitalize<P>]: V }, V extends string>(a: T): T;
|
||||
>foo2 : <T extends { [P in keyof T & string as Capitalize<P>]: V; }, V extends string>(a: T) => T
|
||||
>a : T
|
||||
|
||||
export const r2 = foo2({A: "a"});
|
||||
>r2 : { A: string; }
|
||||
>foo2({A: "a"}) : { A: string; }
|
||||
>foo2 : <T extends { [P in keyof T & string as Capitalize<P>]: V; }, V extends string>(a: T) => T
|
||||
>{A: "a"} : { A: string; }
|
||||
>A : string
|
||||
>"a" : "a"
|
||||
|
||||
7
tests/cases/compiler/circularMappedTypeConstraint.ts
Normal file
7
tests/cases/compiler/circularMappedTypeConstraint.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
// Repro from #56232
|
||||
|
||||
declare function foo2<T extends { [P in keyof T & string as Capitalize<P>]: V }, V extends string>(a: T): T;
|
||||
export const r2 = foo2({A: "a"});
|
||||
Loading…
x
Reference in New Issue
Block a user