Skip to content

Commit 41ec497

Browse files
Andaristjakebailey
andauthored
Defer conditional types with parenthesized multi-element tuple types in extends clause (#56271)
Co-authored-by: Jake Bailey <[email protected]>
1 parent b436976 commit 41ec497

File tree

4 files changed

+223
-3
lines changed

4 files changed

+223
-3
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,7 @@ import {
967967
skipParentheses,
968968
skipTrivia,
969969
skipTypeChecking,
970+
skipTypeParentheses,
970971
some,
971972
SourceFile,
972973
SpreadAssignment,
@@ -18490,7 +18491,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1849018491
return constraint && (isGenericObjectType(constraint) || isGenericIndexType(constraint)) ? cloneTypeParameter(p) : p;
1849118492
}
1849218493

18493-
function isSimpleTupleType(node: TypeNode) {
18494+
function isSimpleTupleType(node: TypeNode): boolean {
1849418495
return isTupleTypeNode(node) && length(node.elements) > 0 &&
1849518496
!some(node.elements, e => isOptionalTypeNode(e) || isRestTypeNode(e) || isNamedTupleMember(e) && !!(e.questionToken || e.dotDotDotToken));
1849618497
}
@@ -18521,11 +18522,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1852118522
if (checkType === wildcardType || extendsType === wildcardType) {
1852218523
return wildcardType;
1852318524
}
18525+
const checkTypeNode = skipTypeParentheses(root.node.checkType);
18526+
const extendsTypeNode = skipTypeParentheses(root.node.extendsType);
1852418527
// When the check and extends types are simple tuple types of the same arity, we defer resolution of the
1852518528
// conditional type when any tuple elements are generic. This is such that non-distributable conditional
1852618529
// types can be written `[X] extends [Y] ? ...` and be deferred similarly to `X extends Y ? ...`.
18527-
const checkTuples = isSimpleTupleType(root.node.checkType) && isSimpleTupleType(root.node.extendsType) &&
18528-
length((root.node.checkType as TupleTypeNode).elements) === length((root.node.extendsType as TupleTypeNode).elements);
18530+
const checkTuples = isSimpleTupleType(checkTypeNode) && isSimpleTupleType(extendsTypeNode) &&
18531+
length((checkTypeNode as TupleTypeNode).elements) === length((extendsTypeNode as TupleTypeNode).elements);
1852918532
const checkTypeDeferred = isDeferredType(checkType, checkTuples);
1853018533
let combinedMapper: TypeMapper | undefined;
1853118534
if (root.inferTypeParameters) {
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//// [tests/cases/compiler/deferredConditionalTypes2.ts] ////
2+
3+
=== deferredConditionalTypes2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/56270
5+
6+
type PositiveInfinity = 1e999;
7+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
8+
9+
type NegativeInfinity = -1e999;
10+
>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30))
11+
12+
export type IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <
13+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
14+
>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 5, 20))
15+
>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 5, 22))
16+
>G : Symbol(G, Decl(deferredConditionalTypes2.ts, 5, 30))
17+
>G : Symbol(G, Decl(deferredConditionalTypes2.ts, 5, 30))
18+
>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 5, 20))
19+
20+
G,
21+
>G : Symbol(G, Decl(deferredConditionalTypes2.ts, 5, 68))
22+
23+
>() => G extends B ? 1 : 2
24+
>G : Symbol(G, Decl(deferredConditionalTypes2.ts, 5, 68))
25+
>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 5, 22))
26+
27+
? true
28+
: false;
29+
30+
export type Add<A extends number, B extends number> = [
31+
>Add : Symbol(Add, Decl(deferredConditionalTypes2.ts, 9, 10))
32+
>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 11, 16))
33+
>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 11, 33))
34+
35+
IsEqual<A, PositiveInfinity>,
36+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
37+
>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 11, 16))
38+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
39+
40+
IsEqual<A, NegativeInfinity>,
41+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
42+
>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 11, 16))
43+
>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30))
44+
45+
IsEqual<B, PositiveInfinity>,
46+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
47+
>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 11, 33))
48+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
49+
50+
IsEqual<B, NegativeInfinity>,
51+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
52+
>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 11, 33))
53+
>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30))
54+
55+
] extends infer R extends [boolean, boolean, boolean, boolean]
56+
>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 16, 15))
57+
58+
? [true, false] extends ([R[0], R[3]])
59+
>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 16, 15))
60+
>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 16, 15))
61+
62+
? PositiveInfinity
63+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
64+
65+
: "failed"
66+
: never;
67+
68+
export type AddWithoutParentheses<A extends number, B extends number> = [
69+
>AddWithoutParentheses : Symbol(AddWithoutParentheses, Decl(deferredConditionalTypes2.ts, 20, 10))
70+
>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 22, 34))
71+
>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 22, 51))
72+
73+
IsEqual<A, PositiveInfinity>,
74+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
75+
>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 22, 34))
76+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
77+
78+
IsEqual<A, NegativeInfinity>,
79+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
80+
>A : Symbol(A, Decl(deferredConditionalTypes2.ts, 22, 34))
81+
>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30))
82+
83+
IsEqual<B, PositiveInfinity>,
84+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
85+
>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 22, 51))
86+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
87+
88+
IsEqual<B, NegativeInfinity>,
89+
>IsEqual : Symbol(IsEqual, Decl(deferredConditionalTypes2.ts, 3, 31))
90+
>B : Symbol(B, Decl(deferredConditionalTypes2.ts, 22, 51))
91+
>NegativeInfinity : Symbol(NegativeInfinity, Decl(deferredConditionalTypes2.ts, 2, 30))
92+
93+
] extends infer R extends [boolean, boolean, boolean, boolean]
94+
>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 27, 15))
95+
96+
? [true, false] extends [R[0], R[3]]
97+
>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 27, 15))
98+
>R : Symbol(R, Decl(deferredConditionalTypes2.ts, 27, 15))
99+
100+
? PositiveInfinity
101+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
102+
103+
: "failed"
104+
: never;
105+
106+
type AddTest0 = Add<PositiveInfinity, PositiveInfinity>;
107+
>AddTest0 : Symbol(AddTest0, Decl(deferredConditionalTypes2.ts, 31, 10))
108+
>Add : Symbol(Add, Decl(deferredConditionalTypes2.ts, 9, 10))
109+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
110+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
111+
112+
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>;
113+
>AddTest1 : Symbol(AddTest1, Decl(deferredConditionalTypes2.ts, 33, 56))
114+
>AddWithoutParentheses : Symbol(AddWithoutParentheses, Decl(deferredConditionalTypes2.ts, 20, 10))
115+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
116+
>PositiveInfinity : Symbol(PositiveInfinity, Decl(deferredConditionalTypes2.ts, 0, 0))
117+
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//// [tests/cases/compiler/deferredConditionalTypes2.ts] ////
2+
3+
=== deferredConditionalTypes2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/56270
5+
6+
type PositiveInfinity = 1e999;
7+
>PositiveInfinity : Infinity
8+
9+
type NegativeInfinity = -1e999;
10+
>NegativeInfinity : -Infinity
11+
>-1e999 : -Infinity
12+
>1e999 : Infinity
13+
14+
export type IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <
15+
>IsEqual : IsEqual<A, B>
16+
17+
G,
18+
>() => G extends B ? 1 : 2
19+
? true
20+
>true : true
21+
22+
: false;
23+
>false : false
24+
25+
export type Add<A extends number, B extends number> = [
26+
>Add : [true, false] extends [IsEqual<A, Infinity>, IsEqual<B, -Infinity>] ? Infinity : "failed"
27+
28+
IsEqual<A, PositiveInfinity>,
29+
IsEqual<A, NegativeInfinity>,
30+
IsEqual<B, PositiveInfinity>,
31+
IsEqual<B, NegativeInfinity>,
32+
] extends infer R extends [boolean, boolean, boolean, boolean]
33+
? [true, false] extends ([R[0], R[3]])
34+
>true : true
35+
>false : false
36+
37+
? PositiveInfinity
38+
: "failed"
39+
: never;
40+
41+
export type AddWithoutParentheses<A extends number, B extends number> = [
42+
>AddWithoutParentheses : [true, false] extends [IsEqual<A, Infinity>, IsEqual<B, -Infinity>] ? Infinity : "failed"
43+
44+
IsEqual<A, PositiveInfinity>,
45+
IsEqual<A, NegativeInfinity>,
46+
IsEqual<B, PositiveInfinity>,
47+
IsEqual<B, NegativeInfinity>,
48+
] extends infer R extends [boolean, boolean, boolean, boolean]
49+
? [true, false] extends [R[0], R[3]]
50+
>true : true
51+
>false : false
52+
53+
? PositiveInfinity
54+
: "failed"
55+
: never;
56+
57+
type AddTest0 = Add<PositiveInfinity, PositiveInfinity>;
58+
>AddTest0 : Infinity
59+
60+
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>;
61+
>AddTest1 : Infinity
62+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/56270
5+
6+
type PositiveInfinity = 1e999;
7+
type NegativeInfinity = -1e999;
8+
9+
export type IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <
10+
G,
11+
>() => G extends B ? 1 : 2
12+
? true
13+
: false;
14+
15+
export type Add<A extends number, B extends number> = [
16+
IsEqual<A, PositiveInfinity>,
17+
IsEqual<A, NegativeInfinity>,
18+
IsEqual<B, PositiveInfinity>,
19+
IsEqual<B, NegativeInfinity>,
20+
] extends infer R extends [boolean, boolean, boolean, boolean]
21+
? [true, false] extends ([R[0], R[3]])
22+
? PositiveInfinity
23+
: "failed"
24+
: never;
25+
26+
export type AddWithoutParentheses<A extends number, B extends number> = [
27+
IsEqual<A, PositiveInfinity>,
28+
IsEqual<A, NegativeInfinity>,
29+
IsEqual<B, PositiveInfinity>,
30+
IsEqual<B, NegativeInfinity>,
31+
] extends infer R extends [boolean, boolean, boolean, boolean]
32+
? [true, false] extends [R[0], R[3]]
33+
? PositiveInfinity
34+
: "failed"
35+
: never;
36+
37+
type AddTest0 = Add<PositiveInfinity, PositiveInfinity>;
38+
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>;

0 commit comments

Comments
 (0)