diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 56c4d8a2114c9..b0ea7ea9ffb04 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -967,6 +967,7 @@ import { skipParentheses, skipTrivia, skipTypeChecking, + skipTypeParentheses, some, SourceFile, SpreadAssignment, @@ -18490,7 +18491,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return constraint && (isGenericObjectType(constraint) || isGenericIndexType(constraint)) ? cloneTypeParameter(p) : p; } - function isSimpleTupleType(node: TypeNode) { + function isSimpleTupleType(node: TypeNode): boolean { return isTupleTypeNode(node) && length(node.elements) > 0 && !some(node.elements, e => isOptionalTypeNode(e) || isRestTypeNode(e) || isNamedTupleMember(e) && !!(e.questionToken || e.dotDotDotToken)); } @@ -18521,11 +18522,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } + const checkTypeNode = skipTypeParentheses(root.node.checkType); + const extendsTypeNode = skipTypeParentheses(root.node.extendsType); // When the check and extends types are simple tuple types of the same arity, we defer resolution of the // conditional type when any tuple elements are generic. This is such that non-distributable conditional // types can be written `[X] extends [Y] ? ...` and be deferred similarly to `X extends Y ? ...`. - const checkTuples = isSimpleTupleType(root.node.checkType) && isSimpleTupleType(root.node.extendsType) && - length((root.node.checkType as TupleTypeNode).elements) === length((root.node.extendsType as TupleTypeNode).elements); + const checkTuples = isSimpleTupleType(checkTypeNode) && isSimpleTupleType(extendsTypeNode) && + length((checkTypeNode as TupleTypeNode).elements) === length((extendsTypeNode as TupleTypeNode).elements); const checkTypeDeferred = isDeferredType(checkType, checkTuples); let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { diff --git a/tests/baselines/reference/deferredConditionalTypes2.symbols b/tests/baselines/reference/deferredConditionalTypes2.symbols new file mode 100644 index 0000000000000..b3abb1b74ba1a --- /dev/null +++ b/tests/baselines/reference/deferredConditionalTypes2.symbols @@ -0,0 +1,117 @@ +//// [tests/cases/compiler/deferredConditionalTypes2.ts] //// + +=== deferredConditionalTypes2.ts === +// https://github.com/microsoft/TypeScript/issues/56270 + +type PositiveInfinity = 1e999; +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + +type NegativeInfinity = -1e999; +>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30)) + +export type IsEqual = (() => G extends A ? 1 : 2) extends < +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 5, 20)) +>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 5, 22)) +>G : Symbol(G, Decl(deferredConditionalTypes2.ts, 5, 30)) +>G : Symbol(G, Decl(deferredConditionalTypes2.ts, 5, 30)) +>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 5, 20)) + + G, +>G : Symbol(G, Decl(deferredConditionalTypes2.ts, 5, 68)) + +>() => G extends B ? 1 : 2 +>G : Symbol(G, Decl(deferredConditionalTypes2.ts, 5, 68)) +>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 5, 22)) + + ? true + : false; + +export type Add = [ +>Add : Symbol(Add, Decl(deferredConditionalTypes2.ts, 9, 10)) +>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 11, 16)) +>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 11, 33)) + + IsEqual, +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 11, 16)) +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + + IsEqual, +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 11, 16)) +>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30)) + + IsEqual, +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 11, 33)) +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + + IsEqual, +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 11, 33)) +>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30)) + +] extends infer R extends [boolean, boolean, boolean, boolean] +>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 16, 15)) + + ? [true, false] extends ([R[0], R[3]]) +>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 16, 15)) +>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 16, 15)) + + ? PositiveInfinity +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + + : "failed" + : never; + +export type AddWithoutParentheses = [ +>AddWithoutParentheses : Symbol(AddWithoutParentheses, Decl(deferredConditionalTypes2.ts, 20, 10)) +>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 22, 34)) +>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 22, 51)) + + IsEqual, +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 22, 34)) +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + + IsEqual, +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 22, 34)) +>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30)) + + IsEqual, +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 22, 51)) +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + + IsEqual, +>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31)) +>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 22, 51)) +>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30)) + +] extends infer R extends [boolean, boolean, boolean, boolean] +>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 27, 15)) + + ? [true, false] extends [R[0], R[3]] +>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 27, 15)) +>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 27, 15)) + + ? PositiveInfinity +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + + : "failed" + : never; + +type AddTest0 = Add; +>AddTest0 : Symbol(AddTest0, Decl(deferredConditionalTypes2.ts, 31, 10)) +>Add : Symbol(Add, Decl(deferredConditionalTypes2.ts, 9, 10)) +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + +type AddTest1 = AddWithoutParentheses; +>AddTest1 : Symbol(AddTest1, Decl(deferredConditionalTypes2.ts, 33, 56)) +>AddWithoutParentheses : Symbol(AddWithoutParentheses, Decl(deferredConditionalTypes2.ts, 20, 10)) +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) +>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0)) + diff --git a/tests/baselines/reference/deferredConditionalTypes2.types b/tests/baselines/reference/deferredConditionalTypes2.types new file mode 100644 index 0000000000000..a14eb9574aa16 --- /dev/null +++ b/tests/baselines/reference/deferredConditionalTypes2.types @@ -0,0 +1,62 @@ +//// [tests/cases/compiler/deferredConditionalTypes2.ts] //// + +=== deferredConditionalTypes2.ts === +// https://github.com/microsoft/TypeScript/issues/56270 + +type PositiveInfinity = 1e999; +>PositiveInfinity : Infinity + +type NegativeInfinity = -1e999; +>NegativeInfinity : -Infinity +>-1e999 : -Infinity +>1e999 : Infinity + +export type IsEqual = (() => G extends A ? 1 : 2) extends < +>IsEqual : IsEqual + + G, +>() => G extends B ? 1 : 2 + ? true +>true : true + + : false; +>false : false + +export type Add = [ +>Add : [true, false] extends [IsEqual, IsEqual] ? Infinity : "failed" + + IsEqual, + IsEqual, + IsEqual, + IsEqual, +] extends infer R extends [boolean, boolean, boolean, boolean] + ? [true, false] extends ([R[0], R[3]]) +>true : true +>false : false + + ? PositiveInfinity + : "failed" + : never; + +export type AddWithoutParentheses = [ +>AddWithoutParentheses : [true, false] extends [IsEqual, IsEqual] ? Infinity : "failed" + + IsEqual, + IsEqual, + IsEqual, + IsEqual, +] extends infer R extends [boolean, boolean, boolean, boolean] + ? [true, false] extends [R[0], R[3]] +>true : true +>false : false + + ? PositiveInfinity + : "failed" + : never; + +type AddTest0 = Add; +>AddTest0 : Infinity + +type AddTest1 = AddWithoutParentheses; +>AddTest1 : Infinity + diff --git a/tests/cases/compiler/deferredConditionalTypes2.ts b/tests/cases/compiler/deferredConditionalTypes2.ts new file mode 100644 index 0000000000000..e1f4f57a2673b --- /dev/null +++ b/tests/cases/compiler/deferredConditionalTypes2.ts @@ -0,0 +1,38 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/56270 + +type PositiveInfinity = 1e999; +type NegativeInfinity = -1e999; + +export type IsEqual = (() => G extends A ? 1 : 2) extends < + G, +>() => G extends B ? 1 : 2 + ? true + : false; + +export type Add = [ + IsEqual, + IsEqual, + IsEqual, + IsEqual, +] extends infer R extends [boolean, boolean, boolean, boolean] + ? [true, false] extends ([R[0], R[3]]) + ? PositiveInfinity + : "failed" + : never; + +export type AddWithoutParentheses = [ + IsEqual, + IsEqual, + IsEqual, + IsEqual, +] extends infer R extends [boolean, boolean, boolean, boolean] + ? [true, false] extends [R[0], R[3]] + ? PositiveInfinity + : "failed" + : never; + +type AddTest0 = Add; +type AddTest1 = AddWithoutParentheses;