From b4749227e2732480913eb7413141faaf1542c13b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Dec 2020 17:59:50 -1000 Subject: [PATCH 1/4] Preserve substitution types in check types of conditional types --- src/compiler/checker.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e470e47b5c3ed..87bf4e36301a8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14374,10 +14374,9 @@ namespace ts { } } // Return a deferred type for a check that is neither definitely true nor definitely false - const erasedCheckType = getActualTypeVariable(checkType); result = createType(TypeFlags.Conditional); result.root = root; - result.checkType = erasedCheckType; + result.checkType = checkType; result.extendsType = extendsType; result.mapper = mapper; result.combinedMapper = combinedMapper; @@ -20210,12 +20209,6 @@ namespace ts { invokeOnce(source, target, inferFromObjectTypes); } } - if (source.flags & TypeFlags.Simplifiable) { - const simplified = getSimplifiedType(source, contravariant); - if (simplified !== source) { - inferFromTypes(simplified, target); - } - } } function inferWithPriority(source: Type, target: Type, newPriority: InferencePriority) { From 7b7b03a9d28658adda136984a78cfcb6b07a0a09 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Dec 2020 18:39:04 -1000 Subject: [PATCH 2/4] Undo changes from #32093 --- src/compiler/checker.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 87bf4e36301a8..bebda41082c3b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14089,13 +14089,6 @@ namespace ts { } } - function unwrapSubstitution(type: Type): Type { - if (type.flags & TypeFlags.Substitution) { - return (type as SubstitutionType).substitute; - } - return type; - } - // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return // the type itself if no transformation is possible. The writing flag indicates that the type is // the target of an assignment. @@ -14107,7 +14100,7 @@ namespace ts { type[cache] = circularConstraintType; // We recursively simplify the object type as it may in turn be an indexed access type. For example, with // '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type. - const objectType = unwrapSubstitution(getSimplifiedType(type.objectType, writing)); + const objectType = getSimplifiedType(type.objectType, writing); const indexType = getSimplifiedType(type.indexType, writing); // T[A | B] -> T[A] | T[B] (reading) // T[A | B] -> T[A] & T[B] (writing) @@ -14325,11 +14318,7 @@ namespace ts { let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None); - // We skip inference of the possible `infer` types unles the `extendsType` _is_ an infer type - // if it was, it's trivial to say that extendsType = checkType, however such a pattern is used to - // "reset" the type being build up during constraint calculation and avoid making an apparently "infinite" constraint - // so in those cases we refain from performing inference and retain the uninfered type parameter - if (!checkTypeInstantiable || !some(root.inferTypeParameters, t => t === extendsType)) { + 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. From 11e4059d4d10cb3ac8ea9818921a9a3346b20710 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 6 Dec 2020 06:49:26 -1000 Subject: [PATCH 3/4] Add regression tests --- .../compiler/recursiveConditionalTypes.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/cases/compiler/recursiveConditionalTypes.ts b/tests/cases/compiler/recursiveConditionalTypes.ts index f3d88cd95728e..5750abe0d5f58 100644 --- a/tests/cases/compiler/recursiveConditionalTypes.ts +++ b/tests/cases/compiler/recursiveConditionalTypes.ts @@ -119,3 +119,23 @@ type Grow2 = T['length'] extends N ? T : function f21(x: Grow1<[], T>, y: Grow2<[], T>) { f21(y, x); // Error } + +// Repros from #41756 + +type ParseSuccess = { rest: R }; + +type ParseManyWhitespace = + S extends ` ${infer R0}` ? + ParseManyWhitespace extends ParseSuccess ? ParseSuccess : null : + ParseSuccess; + +type TP1 = ParseManyWhitespace<" foo">; + +type ParseManyWhitespace2 = + S extends ` ${infer R0}` ? + Helper> : + ParseSuccess; + +type Helper = T extends ParseSuccess ? ParseSuccess : null + +type TP2 = ParseManyWhitespace2<" foo">; From 542a8a2b6bd2c5c644b1f73ead28ee44d6f4c278 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 6 Dec 2020 06:49:33 -1000 Subject: [PATCH 4/4] Accept new baselines --- .../recursiveConditionalTypes.errors.txt | 20 ++++++ .../reference/recursiveConditionalTypes.js | 28 +++++++++ .../recursiveConditionalTypes.symbols | 62 +++++++++++++++++++ .../reference/recursiveConditionalTypes.types | 32 ++++++++++ 4 files changed, 142 insertions(+) diff --git a/tests/baselines/reference/recursiveConditionalTypes.errors.txt b/tests/baselines/reference/recursiveConditionalTypes.errors.txt index b9ae411978f2d..b6b3425f01ead 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.errors.txt +++ b/tests/baselines/reference/recursiveConditionalTypes.errors.txt @@ -189,4 +189,24 @@ tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument !!! error TS2345: Type '[string]' is not assignable to type '[number]'. !!! error TS2345: Type 'string' is not assignable to type 'number'. } + + // Repros from #41756 + + type ParseSuccess = { rest: R }; + + type ParseManyWhitespace = + S extends ` ${infer R0}` ? + ParseManyWhitespace extends ParseSuccess ? ParseSuccess : null : + ParseSuccess; + + type TP1 = ParseManyWhitespace<" foo">; + + type ParseManyWhitespace2 = + S extends ` ${infer R0}` ? + Helper> : + ParseSuccess; + + type Helper = T extends ParseSuccess ? ParseSuccess : null + + type TP2 = ParseManyWhitespace2<" foo">; \ No newline at end of file diff --git a/tests/baselines/reference/recursiveConditionalTypes.js b/tests/baselines/reference/recursiveConditionalTypes.js index 13dd79891645e..4b908af402663 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.js +++ b/tests/baselines/reference/recursiveConditionalTypes.js @@ -116,6 +116,26 @@ type Grow2 = T['length'] extends N ? T : function f21(x: Grow1<[], T>, y: Grow2<[], T>) { f21(y, x); // Error } + +// Repros from #41756 + +type ParseSuccess = { rest: R }; + +type ParseManyWhitespace = + S extends ` ${infer R0}` ? + ParseManyWhitespace extends ParseSuccess ? ParseSuccess : null : + ParseSuccess; + +type TP1 = ParseManyWhitespace<" foo">; + +type ParseManyWhitespace2 = + S extends ` ${infer R0}` ? + Helper> : + ParseSuccess; + +type Helper = T extends ParseSuccess ? ParseSuccess : null + +type TP2 = ParseManyWhitespace2<" foo">; //// [recursiveConditionalTypes.js] @@ -214,3 +234,11 @@ declare function f20(x: Unpack1, y: Unpack2): void; declare type Grow1 = T['length'] extends N ? T : Grow1<[number, ...T], N>; declare type Grow2 = T['length'] extends N ? T : Grow2<[string, ...T], N>; declare function f21(x: Grow1<[], T>, y: Grow2<[], T>): void; +declare type ParseSuccess = { + rest: R; +}; +declare type ParseManyWhitespace = S extends ` ${infer R0}` ? ParseManyWhitespace extends ParseSuccess ? ParseSuccess : null : ParseSuccess; +declare type TP1 = ParseManyWhitespace<" foo">; +declare type ParseManyWhitespace2 = S extends ` ${infer R0}` ? Helper> : ParseSuccess; +declare type Helper = T extends ParseSuccess ? ParseSuccess : null; +declare type TP2 = ParseManyWhitespace2<" foo">; diff --git a/tests/baselines/reference/recursiveConditionalTypes.symbols b/tests/baselines/reference/recursiveConditionalTypes.symbols index c0154a1e1a532..25cf1417b4039 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.symbols +++ b/tests/baselines/reference/recursiveConditionalTypes.symbols @@ -465,3 +465,65 @@ function f21(x: Grow1<[], T>, y: Grow2<[], T>) { >x : Symbol(x, Decl(recursiveConditionalTypes.ts, 114, 31)) } +// Repros from #41756 + +type ParseSuccess = { rest: R }; +>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 120, 18)) +>rest : Symbol(rest, Decl(recursiveConditionalTypes.ts, 120, 39)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 120, 18)) + +type ParseManyWhitespace = +>ParseManyWhitespace : Symbol(ParseManyWhitespace, Decl(recursiveConditionalTypes.ts, 120, 50)) +>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 122, 25)) + + S extends ` ${infer R0}` ? +>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 122, 25)) +>R0 : Symbol(R0, Decl(recursiveConditionalTypes.ts, 123, 23)) + + ParseManyWhitespace extends ParseSuccess ? ParseSuccess : null : +>ParseManyWhitespace : Symbol(ParseManyWhitespace, Decl(recursiveConditionalTypes.ts, 120, 50)) +>R0 : Symbol(R0, Decl(recursiveConditionalTypes.ts, 123, 23)) +>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1)) +>R1 : Symbol(R1, Decl(recursiveConditionalTypes.ts, 124, 58)) +>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1)) +>R1 : Symbol(R1, Decl(recursiveConditionalTypes.ts, 124, 58)) + + ParseSuccess; +>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1)) +>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 122, 25)) + +type TP1 = ParseManyWhitespace<" foo">; +>TP1 : Symbol(TP1, Decl(recursiveConditionalTypes.ts, 125, 24)) +>ParseManyWhitespace : Symbol(ParseManyWhitespace, Decl(recursiveConditionalTypes.ts, 120, 50)) + +type ParseManyWhitespace2 = +>ParseManyWhitespace2 : Symbol(ParseManyWhitespace2, Decl(recursiveConditionalTypes.ts, 127, 39)) +>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 129, 26)) + + S extends ` ${infer R0}` ? +>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 129, 26)) +>R0 : Symbol(R0, Decl(recursiveConditionalTypes.ts, 130, 23)) + + Helper> : +>Helper : Symbol(Helper, Decl(recursiveConditionalTypes.ts, 132, 24)) +>ParseManyWhitespace2 : Symbol(ParseManyWhitespace2, Decl(recursiveConditionalTypes.ts, 127, 39)) +>R0 : Symbol(R0, Decl(recursiveConditionalTypes.ts, 130, 23)) + + ParseSuccess; +>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1)) +>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 129, 26)) + +type Helper = T extends ParseSuccess ? ParseSuccess : null +>Helper : Symbol(Helper, Decl(recursiveConditionalTypes.ts, 132, 24)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 134, 12)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 134, 12)) +>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 134, 45)) +>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 134, 45)) + +type TP2 = ParseManyWhitespace2<" foo">; +>TP2 : Symbol(TP2, Decl(recursiveConditionalTypes.ts, 134, 73)) +>ParseManyWhitespace2 : Symbol(ParseManyWhitespace2, Decl(recursiveConditionalTypes.ts, 127, 39)) + diff --git a/tests/baselines/reference/recursiveConditionalTypes.types b/tests/baselines/reference/recursiveConditionalTypes.types index a797f76b83de5..21f5f758d0ec5 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.types +++ b/tests/baselines/reference/recursiveConditionalTypes.types @@ -310,3 +310,35 @@ function f21(x: Grow1<[], T>, y: Grow2<[], T>) { >x : Grow1<[], T> } +// Repros from #41756 + +type ParseSuccess = { rest: R }; +>ParseSuccess : ParseSuccess +>rest : R + +type ParseManyWhitespace = +>ParseManyWhitespace : ParseManyWhitespace + + S extends ` ${infer R0}` ? + ParseManyWhitespace extends ParseSuccess ? ParseSuccess : null : +>null : null + + ParseSuccess; + +type TP1 = ParseManyWhitespace<" foo">; +>TP1 : ParseSuccess<"foo"> + +type ParseManyWhitespace2 = +>ParseManyWhitespace2 : ParseManyWhitespace2 + + S extends ` ${infer R0}` ? + Helper> : + ParseSuccess; + +type Helper = T extends ParseSuccess ? ParseSuccess : null +>Helper : Helper +>null : null + +type TP2 = ParseManyWhitespace2<" foo">; +>TP2 : ParseSuccess<"foo"> +