Fix non-homomorphic mapped type constraint issues (#41807)

* Less aggressive wildcard check, 'keyof any' constraint for 'infer T' in mapped type constraint position

* Accept new baselines

* Add regression tests
This commit is contained in:
Anders Hejlsberg
2020-12-03 18:36:45 -08:00
committed by GitHub
parent 143d1104ab
commit cd37a327a7
6 changed files with 128 additions and 7 deletions

View File

@@ -12081,6 +12081,11 @@ namespace ts {
else if (grandParent.kind === SyntaxKind.TemplateLiteralTypeSpan) {
inferences = append(inferences, stringType);
}
// When an 'infer T' declaration is in the constraint position of a mapped type, we infer a 'keyof any'
// constraint.
else if (grandParent.kind === SyntaxKind.TypeParameter && grandParent.parent.kind === SyntaxKind.MappedType) {
inferences = append(inferences, keyofConstraintType);
}
}
}
}
@@ -15281,8 +15286,7 @@ namespace ts {
}
}
// If the constraint type of the instantiation is the wildcard type, return the wildcard type.
const result = <MappedType>instantiateAnonymousType(type, mapper);
return getConstraintTypeFromMappedType(result) === wildcardType ? wildcardType : result;
return instantiateType(getConstraintTypeFromMappedType(type), mapper) === wildcardType ? wildcardType : instantiateAnonymousType(type, mapper);
}
function getModifiedReadonlyState(state: boolean, modifiers: MappedTypeModifiers) {

View File

@@ -5,12 +5,11 @@ tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(8,11): error TS2313
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(11,6): error TS2456: Type alias 'Recurse2' circularly references itself.
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(12,11): error TS2313: Type parameter 'K' has a circular constraint.
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(20,19): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(66,25): error TS2313: Type parameter 'P' has a circular constraint.
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(73,5): error TS2502: '"each"' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(73,13): error TS2615: Type of property '"each"' circularly references itself in mapped type '{ [P in keyof ListWidget]: undefined extends ListWidget[P] ? never : P; }'.
==== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts (10 errors) ====
==== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts (9 errors) ====
// Recursive mapped types simply appear empty
type Recurse = {
@@ -92,9 +91,6 @@ tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(73,13): error TS261
type NonOptionalKeys<T> = { [P in keyof T]: undefined extends T[P] ? never : P }[keyof T];
type Child<T> = { [P in NonOptionalKeys<T>]: T[P] }
~~~~~~~~~~~~~~~~~~
!!! error TS2313: Type parameter 'P' has a circular constraint.
!!! related TS2751 tests/cases/conformance/types/mapped/recursiveMappedTypes.ts:73:13: Circularity originates in type at this location.
export interface ListWidget {
"type": "list",
@@ -112,4 +108,16 @@ tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(73,13): error TS261
declare let x: ListChild;
x.type;
// Repros from #41790
export type TV<T, K extends keyof T> = T[K] extends Record<infer E, any> ? E : never;
export type ObjectOrArray<T, K extends keyof any = keyof any> = T[] | Record<K, T | Record<K, T> | T[]>;
export type ThemeValue<K extends keyof ThemeType, ThemeType, TVal = any> =
ThemeType[K] extends TVal[] ? number :
ThemeType[K] extends Record<infer E, TVal> ? E :
ThemeType[K] extends ObjectOrArray<infer F> ? F : never;
export type Foo<T> = T extends { [P in infer E]: any } ? E : never;

View File

@@ -78,6 +78,18 @@ type ListChild = Child<ListWidget>
declare let x: ListChild;
x.type;
// Repros from #41790
export type TV<T, K extends keyof T> = T[K] extends Record<infer E, any> ? E : never;
export type ObjectOrArray<T, K extends keyof any = keyof any> = T[] | Record<K, T | Record<K, T> | T[]>;
export type ThemeValue<K extends keyof ThemeType, ThemeType, TVal = any> =
ThemeType[K] extends TVal[] ? number :
ThemeType[K] extends Record<infer E, TVal> ? E :
ThemeType[K] extends ObjectOrArray<infer F> ? F : never;
export type Foo<T> = T extends { [P in infer E]: any } ? E : never;
//// [recursiveMappedTypes.js]
@@ -108,4 +120,10 @@ export interface ListWidget {
"collapsable"?: boolean;
"each": Child<ListWidget>;
}
export declare type TV<T, K extends keyof T> = T[K] extends Record<infer E, any> ? E : never;
export declare type ObjectOrArray<T, K extends keyof any = keyof any> = T[] | Record<K, T | Record<K, T> | T[]>;
export declare type ThemeValue<K extends keyof ThemeType, ThemeType, TVal = any> = ThemeType[K] extends TVal[] ? number : ThemeType[K] extends Record<infer E, TVal> ? E : ThemeType[K] extends ObjectOrArray<infer F> ? F : never;
export declare type Foo<T> = T extends {
[P in infer E]: any;
} ? E : never;
export {};

View File

@@ -219,3 +219,64 @@ declare let x: ListChild;
x.type;
>x : Symbol(x, Decl(recursiveMappedTypes.ts, 77, 11))
// Repros from #41790
export type TV<T, K extends keyof T> = T[K] extends Record<infer E, any> ? E : never;
>TV : Symbol(TV, Decl(recursiveMappedTypes.ts, 78, 7))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 82, 15))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 82, 17))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 82, 15))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 82, 15))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 82, 17))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>E : Symbol(E, Decl(recursiveMappedTypes.ts, 82, 64))
>E : Symbol(E, Decl(recursiveMappedTypes.ts, 82, 64))
export type ObjectOrArray<T, K extends keyof any = keyof any> = T[] | Record<K, T | Record<K, T> | T[]>;
>ObjectOrArray : Symbol(ObjectOrArray, Decl(recursiveMappedTypes.ts, 82, 85))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 84, 26))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 84, 28))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 84, 26))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 84, 28))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 84, 26))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 84, 28))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 84, 26))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 84, 26))
export type ThemeValue<K extends keyof ThemeType, ThemeType, TVal = any> =
>ThemeValue : Symbol(ThemeValue, Decl(recursiveMappedTypes.ts, 84, 104))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 85, 23))
>ThemeType : Symbol(ThemeType, Decl(recursiveMappedTypes.ts, 85, 49))
>ThemeType : Symbol(ThemeType, Decl(recursiveMappedTypes.ts, 85, 49))
>TVal : Symbol(TVal, Decl(recursiveMappedTypes.ts, 85, 60))
ThemeType[K] extends TVal[] ? number :
>ThemeType : Symbol(ThemeType, Decl(recursiveMappedTypes.ts, 85, 49))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 85, 23))
>TVal : Symbol(TVal, Decl(recursiveMappedTypes.ts, 85, 60))
ThemeType[K] extends Record<infer E, TVal> ? E :
>ThemeType : Symbol(ThemeType, Decl(recursiveMappedTypes.ts, 85, 49))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 85, 23))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>E : Symbol(E, Decl(recursiveMappedTypes.ts, 87, 37))
>TVal : Symbol(TVal, Decl(recursiveMappedTypes.ts, 85, 60))
>E : Symbol(E, Decl(recursiveMappedTypes.ts, 87, 37))
ThemeType[K] extends ObjectOrArray<infer F> ? F : never;
>ThemeType : Symbol(ThemeType, Decl(recursiveMappedTypes.ts, 85, 49))
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 85, 23))
>ObjectOrArray : Symbol(ObjectOrArray, Decl(recursiveMappedTypes.ts, 82, 85))
>F : Symbol(F, Decl(recursiveMappedTypes.ts, 88, 44))
>F : Symbol(F, Decl(recursiveMappedTypes.ts, 88, 44))
export type Foo<T> = T extends { [P in infer E]: any } ? E : never;
>Foo : Symbol(Foo, Decl(recursiveMappedTypes.ts, 88, 60))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 90, 16))
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 90, 16))
>P : Symbol(P, Decl(recursiveMappedTypes.ts, 90, 34))
>E : Symbol(E, Decl(recursiveMappedTypes.ts, 90, 44))
>E : Symbol(E, Decl(recursiveMappedTypes.ts, 90, 44))

View File

@@ -133,3 +133,21 @@ x.type;
>x : Child<ListWidget>
>type : any
// Repros from #41790
export type TV<T, K extends keyof T> = T[K] extends Record<infer E, any> ? E : never;
>TV : TV<T, K>
export type ObjectOrArray<T, K extends keyof any = keyof any> = T[] | Record<K, T | Record<K, T> | T[]>;
>ObjectOrArray : ObjectOrArray<T, K>
export type ThemeValue<K extends keyof ThemeType, ThemeType, TVal = any> =
>ThemeValue : ThemeValue<K, ThemeType, TVal>
ThemeType[K] extends TVal[] ? number :
ThemeType[K] extends Record<infer E, TVal> ? E :
ThemeType[K] extends ObjectOrArray<infer F> ? F : never;
export type Foo<T> = T extends { [P in infer E]: any } ? E : never;
>Foo : Foo<T>

View File

@@ -79,3 +79,15 @@ type ListChild = Child<ListWidget>
declare let x: ListChild;
x.type;
// Repros from #41790
export type TV<T, K extends keyof T> = T[K] extends Record<infer E, any> ? E : never;
export type ObjectOrArray<T, K extends keyof any = keyof any> = T[] | Record<K, T | Record<K, T> | T[]>;
export type ThemeValue<K extends keyof ThemeType, ThemeType, TVal = any> =
ThemeType[K] extends TVal[] ? number :
ThemeType[K] extends Record<infer E, TVal> ? E :
ThemeType[K] extends ObjectOrArray<infer F> ? F : never;
export type Foo<T> = T extends { [P in infer E]: any } ? E : never;