Merge pull request #28851 from Microsoft/deferConditionalTypes

Defer resolution of conditional types with generic check or extends types
This commit is contained in:
Anders Hejlsberg
2018-12-04 15:48:57 -08:00
committed by GitHub
8 changed files with 313 additions and 12 deletions

View File

@@ -9785,14 +9785,11 @@ namespace ts {
if (checkType === wildcardType || extendsType === wildcardType) {
return wildcardType;
}
// If this is a distributive conditional type and the check type is generic we need to defer
// resolution of the conditional type such that a later instantiation will properly distribute
// over union types.
const isDeferred = root.isDistributive && maybeTypeOfKind(checkType, TypeFlags.Instantiable);
const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable);
let combinedMapper: TypeMapper | undefined;
if (root.inferTypeParameters) {
const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None);
if (!isDeferred) {
if (!checkTypeInstantiable) {
// We don't want inferences from constraints as they may cause us to eagerly resolve the
// conditional type instead of deferring resolution. Also, we always want strict function
// types rules (i.e. proper contravariance) for inferences.
@@ -9800,16 +9797,17 @@ namespace ts {
}
combinedMapper = combineTypeMappers(mapper, context);
}
if (!isDeferred) {
if (extendsType.flags & TypeFlags.AnyOrUnknown) {
// Instantiate the extends type including inferences for 'infer T' type parameters
const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType;
// We attempt to resolve the conditional type only when the check and extends types are non-generic
if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable)) {
if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) {
return instantiateType(root.trueType, mapper);
}
// Return union of trueType and falseType for 'any' since it matches anything
if (checkType.flags & TypeFlags.Any) {
return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]);
}
// Instantiate the extends type including inferences for 'infer T' type parameters
const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType;
// Return falseType for a definitely false extends check. We check an instantations of the two
// types with type parameters mapped to the wildcard type, the most permissive instantiations
// possible (the wildcard type is assignable to and from all types). If those are not related,

View File

@@ -212,4 +212,38 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2
type T1 = MaybeTrue<{ b: false }>; // "no"
type T2 = MaybeTrue<{ b: true }>; // "yes"
type T3 = MaybeTrue<{ b: boolean }>; // "yes"
// Repro from #28824
type Union = 'a' | 'b';
type Product<A extends Union, B> = { f1: A, f2: B};
type ProductUnion = Product<'a', 0> | Product<'b', 1>;
// {a: "b"; b: "a"}
type UnionComplement = {
[K in Union]: Exclude<Union, K>
};
type UCA = UnionComplement['a'];
type UCB = UnionComplement['b'];
// {a: "a"; b: "b"}
type UnionComplementComplement = {
[K in Union]: Exclude<Union, Exclude<Union, K>>
};
type UCCA = UnionComplementComplement['a'];
type UCCB = UnionComplementComplement['b'];
// {a: Product<'b', 1>; b: Product<'a', 0>}
type ProductComplement = {
[K in Union]: Exclude<ProductUnion, { f1: K }>
};
type PCA = ProductComplement['a'];
type PCB = ProductComplement['b'];
// {a: Product<'a', 0>; b: Product<'b', 1>}
type ProductComplementComplement = {
[K in Union]: Exclude<ProductUnion, Exclude<ProductUnion, { f1: K }>>
};
type PCCA = ProductComplementComplement['a'];
type PCCB = ProductComplementComplement['b'];

View File

@@ -154,6 +154,40 @@ type T0 = MaybeTrue<{ b: never }> // "no"
type T1 = MaybeTrue<{ b: false }>; // "no"
type T2 = MaybeTrue<{ b: true }>; // "yes"
type T3 = MaybeTrue<{ b: boolean }>; // "yes"
// Repro from #28824
type Union = 'a' | 'b';
type Product<A extends Union, B> = { f1: A, f2: B};
type ProductUnion = Product<'a', 0> | Product<'b', 1>;
// {a: "b"; b: "a"}
type UnionComplement = {
[K in Union]: Exclude<Union, K>
};
type UCA = UnionComplement['a'];
type UCB = UnionComplement['b'];
// {a: "a"; b: "b"}
type UnionComplementComplement = {
[K in Union]: Exclude<Union, Exclude<Union, K>>
};
type UCCA = UnionComplementComplement['a'];
type UCCB = UnionComplementComplement['b'];
// {a: Product<'b', 1>; b: Product<'a', 0>}
type ProductComplement = {
[K in Union]: Exclude<ProductUnion, { f1: K }>
};
type PCA = ProductComplement['a'];
type PCB = ProductComplement['b'];
// {a: Product<'a', 0>; b: Product<'b', 1>}
type ProductComplementComplement = {
[K in Union]: Exclude<ProductUnion, Exclude<ProductUnion, { f1: K }>>
};
type PCCA = ProductComplementComplement['a'];
type PCCB = ProductComplementComplement['b'];
//// [conditionalTypes2.js]
@@ -328,3 +362,33 @@ declare type T2 = MaybeTrue<{
declare type T3 = MaybeTrue<{
b: boolean;
}>;
declare type Union = 'a' | 'b';
declare type Product<A extends Union, B> = {
f1: A;
f2: B;
};
declare type ProductUnion = Product<'a', 0> | Product<'b', 1>;
declare type UnionComplement = {
[K in Union]: Exclude<Union, K>;
};
declare type UCA = UnionComplement['a'];
declare type UCB = UnionComplement['b'];
declare type UnionComplementComplement = {
[K in Union]: Exclude<Union, Exclude<Union, K>>;
};
declare type UCCA = UnionComplementComplement['a'];
declare type UCCB = UnionComplementComplement['b'];
declare type ProductComplement = {
[K in Union]: Exclude<ProductUnion, {
f1: K;
}>;
};
declare type PCA = ProductComplement['a'];
declare type PCB = ProductComplement['b'];
declare type ProductComplementComplement = {
[K in Union]: Exclude<ProductUnion, Exclude<ProductUnion, {
f1: K;
}>>;
};
declare type PCCA = ProductComplementComplement['a'];
declare type PCCB = ProductComplementComplement['b'];

View File

@@ -579,3 +579,109 @@ type T3 = MaybeTrue<{ b: boolean }>; // "yes"
>MaybeTrue : Symbol(MaybeTrue, Decl(conditionalTypes2.ts, 145, 63))
>b : Symbol(b, Decl(conditionalTypes2.ts, 154, 21))
// Repro from #28824
type Union = 'a' | 'b';
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
type Product<A extends Union, B> = { f1: A, f2: B};
>Product : Symbol(Product, Decl(conditionalTypes2.ts, 158, 23))
>A : Symbol(A, Decl(conditionalTypes2.ts, 159, 13))
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
>B : Symbol(B, Decl(conditionalTypes2.ts, 159, 29))
>f1 : Symbol(f1, Decl(conditionalTypes2.ts, 159, 36))
>A : Symbol(A, Decl(conditionalTypes2.ts, 159, 13))
>f2 : Symbol(f2, Decl(conditionalTypes2.ts, 159, 43))
>B : Symbol(B, Decl(conditionalTypes2.ts, 159, 29))
type ProductUnion = Product<'a', 0> | Product<'b', 1>;
>ProductUnion : Symbol(ProductUnion, Decl(conditionalTypes2.ts, 159, 51))
>Product : Symbol(Product, Decl(conditionalTypes2.ts, 158, 23))
>Product : Symbol(Product, Decl(conditionalTypes2.ts, 158, 23))
// {a: "b"; b: "a"}
type UnionComplement = {
>UnionComplement : Symbol(UnionComplement, Decl(conditionalTypes2.ts, 160, 54))
[K in Union]: Exclude<Union, K>
>K : Symbol(K, Decl(conditionalTypes2.ts, 164, 3))
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
>K : Symbol(K, Decl(conditionalTypes2.ts, 164, 3))
};
type UCA = UnionComplement['a'];
>UCA : Symbol(UCA, Decl(conditionalTypes2.ts, 165, 2))
>UnionComplement : Symbol(UnionComplement, Decl(conditionalTypes2.ts, 160, 54))
type UCB = UnionComplement['b'];
>UCB : Symbol(UCB, Decl(conditionalTypes2.ts, 166, 32))
>UnionComplement : Symbol(UnionComplement, Decl(conditionalTypes2.ts, 160, 54))
// {a: "a"; b: "b"}
type UnionComplementComplement = {
>UnionComplementComplement : Symbol(UnionComplementComplement, Decl(conditionalTypes2.ts, 167, 32))
[K in Union]: Exclude<Union, Exclude<Union, K>>
>K : Symbol(K, Decl(conditionalTypes2.ts, 171, 3))
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
>K : Symbol(K, Decl(conditionalTypes2.ts, 171, 3))
};
type UCCA = UnionComplementComplement['a'];
>UCCA : Symbol(UCCA, Decl(conditionalTypes2.ts, 172, 2))
>UnionComplementComplement : Symbol(UnionComplementComplement, Decl(conditionalTypes2.ts, 167, 32))
type UCCB = UnionComplementComplement['b'];
>UCCB : Symbol(UCCB, Decl(conditionalTypes2.ts, 173, 43))
>UnionComplementComplement : Symbol(UnionComplementComplement, Decl(conditionalTypes2.ts, 167, 32))
// {a: Product<'b', 1>; b: Product<'a', 0>}
type ProductComplement = {
>ProductComplement : Symbol(ProductComplement, Decl(conditionalTypes2.ts, 174, 43))
[K in Union]: Exclude<ProductUnion, { f1: K }>
>K : Symbol(K, Decl(conditionalTypes2.ts, 178, 3))
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>ProductUnion : Symbol(ProductUnion, Decl(conditionalTypes2.ts, 159, 51))
>f1 : Symbol(f1, Decl(conditionalTypes2.ts, 178, 39))
>K : Symbol(K, Decl(conditionalTypes2.ts, 178, 3))
};
type PCA = ProductComplement['a'];
>PCA : Symbol(PCA, Decl(conditionalTypes2.ts, 179, 2))
>ProductComplement : Symbol(ProductComplement, Decl(conditionalTypes2.ts, 174, 43))
type PCB = ProductComplement['b'];
>PCB : Symbol(PCB, Decl(conditionalTypes2.ts, 180, 34))
>ProductComplement : Symbol(ProductComplement, Decl(conditionalTypes2.ts, 174, 43))
// {a: Product<'a', 0>; b: Product<'b', 1>}
type ProductComplementComplement = {
>ProductComplementComplement : Symbol(ProductComplementComplement, Decl(conditionalTypes2.ts, 181, 34))
[K in Union]: Exclude<ProductUnion, Exclude<ProductUnion, { f1: K }>>
>K : Symbol(K, Decl(conditionalTypes2.ts, 185, 3))
>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>ProductUnion : Symbol(ProductUnion, Decl(conditionalTypes2.ts, 159, 51))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>ProductUnion : Symbol(ProductUnion, Decl(conditionalTypes2.ts, 159, 51))
>f1 : Symbol(f1, Decl(conditionalTypes2.ts, 185, 61))
>K : Symbol(K, Decl(conditionalTypes2.ts, 185, 3))
};
type PCCA = ProductComplementComplement['a'];
>PCCA : Symbol(PCCA, Decl(conditionalTypes2.ts, 186, 2))
>ProductComplementComplement : Symbol(ProductComplementComplement, Decl(conditionalTypes2.ts, 181, 34))
type PCCB = ProductComplementComplement['b'];
>PCCB : Symbol(PCCB, Decl(conditionalTypes2.ts, 187, 45))
>ProductComplementComplement : Symbol(ProductComplementComplement, Decl(conditionalTypes2.ts, 181, 34))

View File

@@ -366,3 +366,68 @@ type T3 = MaybeTrue<{ b: boolean }>; // "yes"
>T3 : "yes"
>b : boolean
// Repro from #28824
type Union = 'a' | 'b';
>Union : Union
type Product<A extends Union, B> = { f1: A, f2: B};
>Product : Product<A, B>
>f1 : A
>f2 : B
type ProductUnion = Product<'a', 0> | Product<'b', 1>;
>ProductUnion : ProductUnion
// {a: "b"; b: "a"}
type UnionComplement = {
>UnionComplement : UnionComplement
[K in Union]: Exclude<Union, K>
};
type UCA = UnionComplement['a'];
>UCA : "b"
type UCB = UnionComplement['b'];
>UCB : "a"
// {a: "a"; b: "b"}
type UnionComplementComplement = {
>UnionComplementComplement : UnionComplementComplement
[K in Union]: Exclude<Union, Exclude<Union, K>>
};
type UCCA = UnionComplementComplement['a'];
>UCCA : "a"
type UCCB = UnionComplementComplement['b'];
>UCCB : "b"
// {a: Product<'b', 1>; b: Product<'a', 0>}
type ProductComplement = {
>ProductComplement : ProductComplement
[K in Union]: Exclude<ProductUnion, { f1: K }>
>f1 : K
};
type PCA = ProductComplement['a'];
>PCA : Product<"b", 1>
type PCB = ProductComplement['b'];
>PCB : Product<"a", 0>
// {a: Product<'a', 0>; b: Product<'b', 1>}
type ProductComplementComplement = {
>ProductComplementComplement : ProductComplementComplement
[K in Union]: Exclude<ProductUnion, Exclude<ProductUnion, { f1: K }>>
>f1 : K
};
type PCCA = ProductComplementComplement['a'];
>PCCA : Product<"a", 0>
type PCCB = ProductComplementComplement['b'];
>PCCB : Product<"b", 1>

View File

@@ -15,7 +15,7 @@ type AProp<T extends { a: string }> = T
>a : string
declare function myBug<
>myBug : <T extends { [K in keyof T]: T[K]; }>(arg: T) => T
>myBug : <T extends { [K in keyof T]: T[K] extends infer U ? U : never; }>(arg: T) => T
T extends { [K in keyof T]: T[K] extends AProp<infer U> ? U : never }
>(arg: T): T
@@ -24,7 +24,7 @@ declare function myBug<
const out = myBug({obj1: {a: "test"}})
>out : { obj1: { a: string; }; }
>myBug({obj1: {a: "test"}}) : { obj1: { a: string; }; }
>myBug : <T extends { [K in keyof T]: T[K]; }>(arg: T) => T
>myBug : <T extends { [K in keyof T]: T[K] extends infer U ? U : never; }>(arg: T) => T
>{obj1: {a: "test"}} : { obj1: { a: string; }; }
>obj1 : { a: string; }
>{a: "test"} : { a: string; }

View File

@@ -76,7 +76,7 @@ type T31<T> = T extends unknown ? true : false; // Deferred (so it distributes)
>false : false
type T32<T> = never extends T ? true : false; // true
>T32 : true
>T32 : T32<T>
>true : true
>false : false

View File

@@ -156,3 +156,37 @@ type T0 = MaybeTrue<{ b: never }> // "no"
type T1 = MaybeTrue<{ b: false }>; // "no"
type T2 = MaybeTrue<{ b: true }>; // "yes"
type T3 = MaybeTrue<{ b: boolean }>; // "yes"
// Repro from #28824
type Union = 'a' | 'b';
type Product<A extends Union, B> = { f1: A, f2: B};
type ProductUnion = Product<'a', 0> | Product<'b', 1>;
// {a: "b"; b: "a"}
type UnionComplement = {
[K in Union]: Exclude<Union, K>
};
type UCA = UnionComplement['a'];
type UCB = UnionComplement['b'];
// {a: "a"; b: "b"}
type UnionComplementComplement = {
[K in Union]: Exclude<Union, Exclude<Union, K>>
};
type UCCA = UnionComplementComplement['a'];
type UCCB = UnionComplementComplement['b'];
// {a: Product<'b', 1>; b: Product<'a', 0>}
type ProductComplement = {
[K in Union]: Exclude<ProductUnion, { f1: K }>
};
type PCA = ProductComplement['a'];
type PCB = ProductComplement['b'];
// {a: Product<'a', 0>; b: Product<'b', 1>}
type ProductComplementComplement = {
[K in Union]: Exclude<ProductUnion, Exclude<ProductUnion, { f1: K }>>
};
type PCCA = ProductComplementComplement['a'];
type PCCB = ProductComplementComplement['b'];