From b032e063cd3b8b55f738e832e8b8bcd781b5e4e2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 4 Dec 2018 13:06:53 -0800 Subject: [PATCH 1/4] Resolve conditional type only when check and extends types are non-generic --- src/compiler/checker.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 982da682be09c..224cde1f5ea3f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9784,14 +9784,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. @@ -9799,16 +9796,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, From 7c727365e406203d94235f3b18cd5a87b4ff2617 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 4 Dec 2018 13:10:58 -0800 Subject: [PATCH 2/4] Accept new baselines --- tests/baselines/reference/infiniteConstraints.types | 4 ++-- tests/baselines/reference/unknownType1.types | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/infiniteConstraints.types b/tests/baselines/reference/infiniteConstraints.types index e23e138c820d6..c7d4c07ff4b3d 100644 --- a/tests/baselines/reference/infiniteConstraints.types +++ b/tests/baselines/reference/infiniteConstraints.types @@ -15,7 +15,7 @@ type AProp = T >a : string declare function myBug< ->myBug : (arg: T) => T +>myBug : (arg: T) => T T extends { [K in keyof T]: T[K] extends AProp ? 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 : (arg: T) => T +>myBug : (arg: T) => T >{obj1: {a: "test"}} : { obj1: { a: string; }; } >obj1 : { a: string; } >{a: "test"} : { a: string; } diff --git a/tests/baselines/reference/unknownType1.types b/tests/baselines/reference/unknownType1.types index aaaf73bb7098a..bf177d4fde863 100644 --- a/tests/baselines/reference/unknownType1.types +++ b/tests/baselines/reference/unknownType1.types @@ -76,7 +76,7 @@ type T31 = T extends unknown ? true : false; // Deferred (so it distributes) >false : false type T32 = never extends T ? true : false; // true ->T32 : true +>T32 : T32 >true : true >false : false From 3e2f130b3c4815889c346bc1c971e14071118454 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 4 Dec 2018 13:18:04 -0800 Subject: [PATCH 3/4] Add regression test --- .../types/conditional/conditionalTypes2.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/cases/conformance/types/conditional/conditionalTypes2.ts b/tests/cases/conformance/types/conditional/conditionalTypes2.ts index 2fd13ceff85cf..4b65b5ddeb202 100644 --- a/tests/cases/conformance/types/conditional/conditionalTypes2.ts +++ b/tests/cases/conformance/types/conditional/conditionalTypes2.ts @@ -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 = { f1: A, f2: B}; +type ProductUnion = Product<'a', 0> | Product<'b', 1>; + +// {a: "b"; b: "a"} +type UnionComplement = { + [K in Union]: Exclude +}; +type UCA = UnionComplement['a']; +type UCB = UnionComplement['b']; + +// {a: "a"; b: "b"} +type UnionComplementComplement = { + [K in Union]: Exclude> +}; +type UCCA = UnionComplementComplement['a']; +type UCCB = UnionComplementComplement['b']; + +// {a: Product<'b', 1>; b: Product<'a', 0>} +type ProductComplement = { + [K in Union]: Exclude +}; +type PCA = ProductComplement['a']; +type PCB = ProductComplement['b']; + +// {a: Product<'a', 0>; b: Product<'b', 1>} +type ProductComplementComplement = { + [K in Union]: Exclude> +}; +type PCCA = ProductComplementComplement['a']; +type PCCB = ProductComplementComplement['b']; From 3afa86fd9c0d56820ad27447bac6e1de7db90a26 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 4 Dec 2018 13:18:13 -0800 Subject: [PATCH 4/4] Accept new baselines --- .../reference/conditionalTypes2.errors.txt | 34 ++++++ .../baselines/reference/conditionalTypes2.js | 64 +++++++++++ .../reference/conditionalTypes2.symbols | 106 ++++++++++++++++++ .../reference/conditionalTypes2.types | 65 +++++++++++ 4 files changed, 269 insertions(+) diff --git a/tests/baselines/reference/conditionalTypes2.errors.txt b/tests/baselines/reference/conditionalTypes2.errors.txt index 322cbe1e7bb47..a1a23b6da1870 100644 --- a/tests/baselines/reference/conditionalTypes2.errors.txt +++ b/tests/baselines/reference/conditionalTypes2.errors.txt @@ -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 = { f1: A, f2: B}; + type ProductUnion = Product<'a', 0> | Product<'b', 1>; + + // {a: "b"; b: "a"} + type UnionComplement = { + [K in Union]: Exclude + }; + type UCA = UnionComplement['a']; + type UCB = UnionComplement['b']; + + // {a: "a"; b: "b"} + type UnionComplementComplement = { + [K in Union]: Exclude> + }; + type UCCA = UnionComplementComplement['a']; + type UCCB = UnionComplementComplement['b']; + + // {a: Product<'b', 1>; b: Product<'a', 0>} + type ProductComplement = { + [K in Union]: Exclude + }; + type PCA = ProductComplement['a']; + type PCB = ProductComplement['b']; + + // {a: Product<'a', 0>; b: Product<'b', 1>} + type ProductComplementComplement = { + [K in Union]: Exclude> + }; + type PCCA = ProductComplementComplement['a']; + type PCCB = ProductComplementComplement['b']; \ No newline at end of file diff --git a/tests/baselines/reference/conditionalTypes2.js b/tests/baselines/reference/conditionalTypes2.js index 02581ab0015b6..4f4f35e821afb 100644 --- a/tests/baselines/reference/conditionalTypes2.js +++ b/tests/baselines/reference/conditionalTypes2.js @@ -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 = { f1: A, f2: B}; +type ProductUnion = Product<'a', 0> | Product<'b', 1>; + +// {a: "b"; b: "a"} +type UnionComplement = { + [K in Union]: Exclude +}; +type UCA = UnionComplement['a']; +type UCB = UnionComplement['b']; + +// {a: "a"; b: "b"} +type UnionComplementComplement = { + [K in Union]: Exclude> +}; +type UCCA = UnionComplementComplement['a']; +type UCCB = UnionComplementComplement['b']; + +// {a: Product<'b', 1>; b: Product<'a', 0>} +type ProductComplement = { + [K in Union]: Exclude +}; +type PCA = ProductComplement['a']; +type PCB = ProductComplement['b']; + +// {a: Product<'a', 0>; b: Product<'b', 1>} +type ProductComplementComplement = { + [K in Union]: Exclude> +}; +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 = { + f1: A; + f2: B; +}; +declare type ProductUnion = Product<'a', 0> | Product<'b', 1>; +declare type UnionComplement = { + [K in Union]: Exclude; +}; +declare type UCA = UnionComplement['a']; +declare type UCB = UnionComplement['b']; +declare type UnionComplementComplement = { + [K in Union]: Exclude>; +}; +declare type UCCA = UnionComplementComplement['a']; +declare type UCCB = UnionComplementComplement['b']; +declare type ProductComplement = { + [K in Union]: Exclude; +}; +declare type PCA = ProductComplement['a']; +declare type PCB = ProductComplement['b']; +declare type ProductComplementComplement = { + [K in Union]: Exclude>; +}; +declare type PCCA = ProductComplementComplement['a']; +declare type PCCB = ProductComplementComplement['b']; diff --git a/tests/baselines/reference/conditionalTypes2.symbols b/tests/baselines/reference/conditionalTypes2.symbols index b9098616f0fda..b164d26e45089 100644 --- a/tests/baselines/reference/conditionalTypes2.symbols +++ b/tests/baselines/reference/conditionalTypes2.symbols @@ -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 = { 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 +>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> +>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 +>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> +>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)) + diff --git a/tests/baselines/reference/conditionalTypes2.types b/tests/baselines/reference/conditionalTypes2.types index e500d21d36b5b..aac6ba47475c6 100644 --- a/tests/baselines/reference/conditionalTypes2.types +++ b/tests/baselines/reference/conditionalTypes2.types @@ -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 = { f1: A, f2: B}; +>Product : Product +>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 +}; +type UCA = UnionComplement['a']; +>UCA : "b" + +type UCB = UnionComplement['b']; +>UCB : "a" + +// {a: "a"; b: "b"} +type UnionComplementComplement = { +>UnionComplementComplement : UnionComplementComplement + + [K in Union]: Exclude> +}; +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 +>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> +>f1 : K + +}; +type PCCA = ProductComplementComplement['a']; +>PCCA : Product<"a", 0> + +type PCCB = ProductComplementComplement['b']; +>PCCB : Product<"b", 1> +