Defer conditional types with parenthesized multi-element tuple types in extends clause (#56271)

Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
This commit is contained in:
Mateusz Burzyński
2023-12-08 21:21:04 +01:00
committed by GitHub
parent b436976bd3
commit 41ec497211
4 changed files with 223 additions and 3 deletions

View File

@@ -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) {

View File

@@ -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<A, B> = (<G>() => 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<A extends number, B extends number> = [
>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<A, PositiveInfinity>,
>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<A, NegativeInfinity>,
>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<B, PositiveInfinity>,
>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<B, NegativeInfinity>,
>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<A extends number, B extends number> = [
>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<A, PositiveInfinity>,
>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<A, NegativeInfinity>,
>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<B, PositiveInfinity>,
>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<B, NegativeInfinity>,
>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<PositiveInfinity, PositiveInfinity>;
>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<PositiveInfinity, PositiveInfinity>;
>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))

View File

@@ -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<A, B> = (<G>() => G extends A ? 1 : 2) extends <
>IsEqual : IsEqual<A, B>
G,
>() => G extends B ? 1 : 2
? true
>true : true
: false;
>false : false
export type Add<A extends number, B extends number> = [
>Add : [true, false] extends [IsEqual<A, Infinity>, IsEqual<B, -Infinity>] ? Infinity : "failed"
IsEqual<A, PositiveInfinity>,
IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>,
IsEqual<B, NegativeInfinity>,
] 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<A extends number, B extends number> = [
>AddWithoutParentheses : [true, false] extends [IsEqual<A, Infinity>, IsEqual<B, -Infinity>] ? Infinity : "failed"
IsEqual<A, PositiveInfinity>,
IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>,
IsEqual<B, NegativeInfinity>,
] 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<PositiveInfinity, PositiveInfinity>;
>AddTest0 : Infinity
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>;
>AddTest1 : Infinity

View File

@@ -0,0 +1,38 @@
// @strict: true
// @noEmit: true
// https://github.com/microsoft/TypeScript/issues/56270
type PositiveInfinity = 1e999;
type NegativeInfinity = -1e999;
export type IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <
G,
>() => G extends B ? 1 : 2
? true
: false;
export type Add<A extends number, B extends number> = [
IsEqual<A, PositiveInfinity>,
IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>,
IsEqual<B, NegativeInfinity>,
] extends infer R extends [boolean, boolean, boolean, boolean]
? [true, false] extends ([R[0], R[3]])
? PositiveInfinity
: "failed"
: never;
export type AddWithoutParentheses<A extends number, B extends number> = [
IsEqual<A, PositiveInfinity>,
IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>,
IsEqual<B, NegativeInfinity>,
] extends infer R extends [boolean, boolean, boolean, boolean]
? [true, false] extends [R[0], R[3]]
? PositiveInfinity
: "failed"
: never;
type AddTest0 = Add<PositiveInfinity, PositiveInfinity>;
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>;