From ee33225b41aff2e0332baf51e9e36d08d1486534 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 18 Jul 2024 20:02:47 -0700 Subject: [PATCH 1/6] add check for >, >=, <, <= for unconstrained types --- src/compiler/checker.ts | 10 +- .../unconstrainedTypeComparison.errors.txt | 109 ++++++++++ .../unconstrainedTypeComparison.symbols | 156 ++++++++++++++ .../unconstrainedTypeComparison.types | 195 ++++++++++++++++++ .../compiler/unconstrainedTypeComparison.ts | 50 +++++ 5 files changed, 518 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/unconstrainedTypeComparison.errors.txt create mode 100644 tests/baselines/reference/unconstrainedTypeComparison.symbols create mode 100644 tests/baselines/reference/unconstrainedTypeComparison.types create mode 100644 tests/cases/compiler/unconstrainedTypeComparison.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7cde9314450eb..cd6dba0d5cdb8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27493,6 +27493,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type === unknownUnionType ? unknownType : type; } + // use to determine if a parameter may be undefined or null (or is unknown/unconstrained) + function getUnknownIfMaybeUnknown(type: Type) { + return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownType : type; + } + function getTypeWithDefault(type: Type, defaultExpression: Expression) { return defaultExpression ? getUnionType([getNonUndefinedType(type), getTypeOfExpression(defaultExpression)]) : @@ -39677,8 +39682,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanEqualsToken: if (checkForDisallowedESSymbolOperand(operator)) { - leftType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(leftType, left)); - rightType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(rightType, right)); + + leftType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(getUnknownIfMaybeUnknown(leftType), left)); + rightType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(getUnknownIfMaybeUnknown(rightType), right)); reportOperatorErrorUnless((left, right) => { if (isTypeAny(left) || isTypeAny(right)) { return true; diff --git a/tests/baselines/reference/unconstrainedTypeComparison.errors.txt b/tests/baselines/reference/unconstrainedTypeComparison.errors.txt new file mode 100644 index 0000000000000..f662d4294bb0f --- /dev/null +++ b/tests/baselines/reference/unconstrainedTypeComparison.errors.txt @@ -0,0 +1,109 @@ +unconstrainedTypeComparison.ts(2,12): error TS18046: 'a' is of type 'unknown'. +unconstrainedTypeComparison.ts(2,16): error TS18046: 'b' is of type 'unknown'. +unconstrainedTypeComparison.ts(6,12): error TS18049: 'a' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(6,16): error TS18049: 'b' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(10,12): error TS18046: 'a' is of type 'unknown'. +unconstrainedTypeComparison.ts(10,16): error TS18046: 'b' is of type 'unknown'. +unconstrainedTypeComparison.ts(14,12): error TS18046: 'a' is of type 'unknown'. +unconstrainedTypeComparison.ts(14,16): error TS18046: 'b' is of type 'unknown'. +unconstrainedTypeComparison.ts(18,12): error TS18049: 'a' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(18,16): error TS18049: 'b' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(22,12): error TS18046: 'a' is of type 'unknown'. +unconstrainedTypeComparison.ts(22,16): error TS18046: 'b' is of type 'unknown'. +unconstrainedTypeComparison.ts(26,12): error TS18048: 'a' is possibly 'undefined'. +unconstrainedTypeComparison.ts(26,16): error TS18048: 'b' is possibly 'undefined'. +unconstrainedTypeComparison.ts(30,12): error TS18047: 'a' is possibly 'null'. +unconstrainedTypeComparison.ts(30,16): error TS18047: 'b' is possibly 'null'. +unconstrainedTypeComparison.ts(34,12): error TS18049: 'a' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(34,16): error TS18049: 'b' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(45,12): error TS18047: 'a' is possibly 'null'. +unconstrainedTypeComparison.ts(45,16): error TS18048: 'b' is possibly 'undefined'. + + +==== unconstrainedTypeComparison.ts (20 errors) ==== + function f1(a: T, b: T): boolean { + return a > b; + ~ +!!! error TS18046: 'a' is of type 'unknown'. + ~ +!!! error TS18046: 'b' is of type 'unknown'. + } + + function f2(a: T, b: T): boolean { + return a > b; + ~ +!!! error TS18049: 'a' is possibly 'null' or 'undefined'. + ~ +!!! error TS18049: 'b' is possibly 'null' or 'undefined'. + } + + function f3(a: T, b: T): boolean { + return a > b; + ~ +!!! error TS18046: 'a' is of type 'unknown'. + ~ +!!! error TS18046: 'b' is of type 'unknown'. + } + + function f4(a: U, b: U): boolean { + return a > b; + ~ +!!! error TS18046: 'a' is of type 'unknown'. + ~ +!!! error TS18046: 'b' is of type 'unknown'. + } + + function f5(a: U, b: U): boolean { + return a > b; + ~ +!!! error TS18049: 'a' is possibly 'null' or 'undefined'. + ~ +!!! error TS18049: 'b' is possibly 'null' or 'undefined'. + } + + function f6(a: U, b: U): boolean { + return a > b; + ~ +!!! error TS18046: 'a' is of type 'unknown'. + ~ +!!! error TS18046: 'b' is of type 'unknown'. + } + + function f7(a: U, b: U): boolean { + return a > b; + ~ +!!! error TS18048: 'a' is possibly 'undefined'. + ~ +!!! error TS18048: 'b' is possibly 'undefined'. + } + + function f8(a: U, b: U): boolean { + return a > b; + ~ +!!! error TS18047: 'a' is possibly 'null'. + ~ +!!! error TS18047: 'b' is possibly 'null'. + } + + function f9(a: U, b: U): boolean { + return a > b; + ~ +!!! error TS18049: 'a' is possibly 'null' or 'undefined'. + ~ +!!! error TS18049: 'b' is possibly 'null' or 'undefined'. + } + + + function compare(a: T, b: T): boolean { + if (a === undefined) { + return false; + } + if (b === null) { + return false; + } + return a > b; + ~ +!!! error TS18047: 'a' is possibly 'null'. + ~ +!!! error TS18048: 'b' is possibly 'undefined'. + } \ No newline at end of file diff --git a/tests/baselines/reference/unconstrainedTypeComparison.symbols b/tests/baselines/reference/unconstrainedTypeComparison.symbols new file mode 100644 index 0000000000000..cc45524452a36 --- /dev/null +++ b/tests/baselines/reference/unconstrainedTypeComparison.symbols @@ -0,0 +1,156 @@ +//// [tests/cases/compiler/unconstrainedTypeComparison.ts] //// + +=== unconstrainedTypeComparison.ts === +function f1(a: T, b: T): boolean { +>f1 : Symbol(f1, Decl(unconstrainedTypeComparison.ts, 0, 0)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 0, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 0, 15)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 0, 12)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 0, 20)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 0, 12)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 0, 15)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 0, 20)) +} + +function f2(a: T, b: T): boolean { +>f2 : Symbol(f2, Decl(unconstrainedTypeComparison.ts, 2, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 4, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 4, 45)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 4, 12)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 4, 50)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 4, 12)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 4, 45)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 4, 50)) +} + +function f3(a: T, b: T): boolean { +>f3 : Symbol(f3, Decl(unconstrainedTypeComparison.ts, 6, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 8, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 8, 31)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 8, 12)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 8, 36)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 8, 12)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 8, 31)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 8, 36)) +} + +function f4(a: U, b: U): boolean { +>f4 : Symbol(f4, Decl(unconstrainedTypeComparison.ts, 10, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 12, 12)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 12, 14)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 12, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 12, 28)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 12, 14)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 12, 33)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 12, 14)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 12, 28)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 12, 33)) +} + +function f5(a: U, b: U): boolean { +>f5 : Symbol(f5, Decl(unconstrainedTypeComparison.ts, 14, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 16, 12)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 16, 44)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 16, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 16, 58)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 16, 44)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 16, 63)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 16, 44)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 16, 58)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 16, 63)) +} + +function f6(a: U, b: U): boolean { +>f6 : Symbol(f6, Decl(unconstrainedTypeComparison.ts, 18, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 20, 12)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 20, 30)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 20, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 20, 44)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 20, 30)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 20, 49)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 20, 30)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 20, 44)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 20, 49)) +} + +function f7(a: U, b: U): boolean { +>f7 : Symbol(f7, Decl(unconstrainedTypeComparison.ts, 22, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 24, 12)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 24, 37)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 24, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 24, 51)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 24, 37)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 24, 56)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 24, 37)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 24, 51)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 24, 56)) +} + +function f8(a: U, b: U): boolean { +>f8 : Symbol(f8, Decl(unconstrainedTypeComparison.ts, 26, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 28, 12)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 28, 32)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 28, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 28, 46)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 28, 32)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 28, 51)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 28, 32)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 28, 46)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 28, 51)) +} + +function f9(a: U, b: U): boolean { +>f9 : Symbol(f9, Decl(unconstrainedTypeComparison.ts, 30, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 32, 12)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 32, 39)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 32, 12)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 32, 53)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 32, 39)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 32, 58)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 32, 39)) + + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 32, 53)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 32, 58)) +} + + +function compare(a: T, b: T): boolean { +>compare : Symbol(compare, Decl(unconstrainedTypeComparison.ts, 34, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17)) + + if (a === undefined) { +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20)) +>undefined : Symbol(undefined) + + return false; + } + if (b === null) { +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25)) + + return false; + } + return a > b; +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25)) +} diff --git a/tests/baselines/reference/unconstrainedTypeComparison.types b/tests/baselines/reference/unconstrainedTypeComparison.types new file mode 100644 index 0000000000000..b942d21aef957 --- /dev/null +++ b/tests/baselines/reference/unconstrainedTypeComparison.types @@ -0,0 +1,195 @@ +//// [tests/cases/compiler/unconstrainedTypeComparison.ts] //// + +=== unconstrainedTypeComparison.ts === +function f1(a: T, b: T): boolean { +>f1 : (a: T, b: T) => boolean +> : ^ ^^ ^^ ^^ ^^ ^^^^^ +>a : T +> : ^ +>b : T +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : T +> : ^ +>b : T +> : ^ +} + +function f2(a: T, b: T): boolean { +>f2 : (a: T, b: T) => boolean +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>a : T +> : ^ +>b : T +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : T +> : ^ +>b : T +> : ^ +} + +function f3(a: T, b: T): boolean { +>f3 : (a: T, b: T) => boolean +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>a : T +> : ^ +>b : T +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : T +> : ^ +>b : T +> : ^ +} + +function f4(a: U, b: U): boolean { +>f4 : (a: U, b: U) => boolean +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>a : U +> : ^ +>b : U +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : U +> : ^ +>b : U +> : ^ +} + +function f5(a: U, b: U): boolean { +>f5 : (a: U, b: U) => boolean +> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>a : U +> : ^ +>b : U +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : U +> : ^ +>b : U +> : ^ +} + +function f6(a: U, b: U): boolean { +>f6 : (a: U, b: U) => boolean +> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>a : U +> : ^ +>b : U +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : U +> : ^ +>b : U +> : ^ +} + +function f7(a: U, b: U): boolean { +>f7 : (a: U, b: U) => boolean +> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>a : U +> : ^ +>b : U +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : U +> : ^ +>b : U +> : ^ +} + +function f8(a: U, b: U): boolean { +>f8 : (a: U, b: U) => boolean +> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>a : U +> : ^ +>b : U +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : U +> : ^ +>b : U +> : ^ +} + +function f9(a: U, b: U): boolean { +>f9 : (a: U, b: U) => boolean +> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>a : U +> : ^ +>b : U +> : ^ + + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : U +> : ^ +>b : U +> : ^ +} + + +function compare(a: T, b: T): boolean { +>compare : (a: T, b: T) => boolean +> : ^ ^^ ^^ ^^ ^^ ^^^^^ +>a : T +> : ^ +>b : T +> : ^ + + if (a === undefined) { +>a === undefined : boolean +> : ^^^^^^^ +>a : T +> : ^ +>undefined : undefined +> : ^^^^^^^^^ + + return false; +>false : false +> : ^^^^^ + } + if (b === null) { +>b === null : boolean +> : ^^^^^^^ +>b : T +> : ^ + + return false; +>false : false +> : ^^^^^ + } + return a > b; +>a > b : boolean +> : ^^^^^^^ +>a : T & ({} | null) +> : ^^^^^^^^^^^^^^^ +>b : T & ({} | undefined) +> : ^^^^^^^^^^^^^^^^^^^^ +} diff --git a/tests/cases/compiler/unconstrainedTypeComparison.ts b/tests/cases/compiler/unconstrainedTypeComparison.ts new file mode 100644 index 0000000000000..c2f197ff38fa5 --- /dev/null +++ b/tests/cases/compiler/unconstrainedTypeComparison.ts @@ -0,0 +1,50 @@ + +// @strict: true +// @noemit: true + +function f1(a: T, b: T): boolean { + return a > b; +} + +function f2(a: T, b: T): boolean { + return a > b; +} + +function f3(a: T, b: T): boolean { + return a > b; +} + +function f4(a: U, b: U): boolean { + return a > b; +} + +function f5(a: U, b: U): boolean { + return a > b; +} + +function f6(a: U, b: U): boolean { + return a > b; +} + +function f7(a: U, b: U): boolean { + return a > b; +} + +function f8(a: U, b: U): boolean { + return a > b; +} + +function f9(a: U, b: U): boolean { + return a > b; +} + + +function compare(a: T, b: T): boolean { + if (a === undefined) { + return false; + } + if (b === null) { + return false; + } + return a > b; +} \ No newline at end of file From fca2ca07746f29e78a718bed5fb7fd8a81539c49 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 18 Jul 2024 21:29:56 -0700 Subject: [PATCH 2/6] fmt --- src/compiler/checker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cd6dba0d5cdb8..858904c90cbe2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39682,7 +39682,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanEqualsToken: if (checkForDisallowedESSymbolOperand(operator)) { - leftType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(getUnknownIfMaybeUnknown(leftType), left)); rightType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(getUnknownIfMaybeUnknown(rightType), right)); reportOperatorErrorUnless((left, right) => { From 1fa487eafe81d178b683e1f8aa5db6e104f9faa2 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 19 Jul 2024 13:38:55 -0700 Subject: [PATCH 3/6] change error to `possibly null or undefined` --- src/compiler/checker.ts | 2 +- .../unconstrainedTypeComparison.errors.txt | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 858904c90cbe2..0a47b0e0f0ce5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27495,7 +27495,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // use to determine if a parameter may be undefined or null (or is unknown/unconstrained) function getUnknownIfMaybeUnknown(type: Type) { - return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownType : type; + return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownUnionType : type; } function getTypeWithDefault(type: Type, defaultExpression: Expression) { diff --git a/tests/baselines/reference/unconstrainedTypeComparison.errors.txt b/tests/baselines/reference/unconstrainedTypeComparison.errors.txt index f662d4294bb0f..a2d250fdb0f48 100644 --- a/tests/baselines/reference/unconstrainedTypeComparison.errors.txt +++ b/tests/baselines/reference/unconstrainedTypeComparison.errors.txt @@ -1,11 +1,11 @@ -unconstrainedTypeComparison.ts(2,12): error TS18046: 'a' is of type 'unknown'. -unconstrainedTypeComparison.ts(2,16): error TS18046: 'b' is of type 'unknown'. +unconstrainedTypeComparison.ts(2,12): error TS18049: 'a' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(2,16): error TS18049: 'b' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(6,12): error TS18049: 'a' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(6,16): error TS18049: 'b' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(10,12): error TS18046: 'a' is of type 'unknown'. unconstrainedTypeComparison.ts(10,16): error TS18046: 'b' is of type 'unknown'. -unconstrainedTypeComparison.ts(14,12): error TS18046: 'a' is of type 'unknown'. -unconstrainedTypeComparison.ts(14,16): error TS18046: 'b' is of type 'unknown'. +unconstrainedTypeComparison.ts(14,12): error TS18049: 'a' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(14,16): error TS18049: 'b' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(18,12): error TS18049: 'a' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(18,16): error TS18049: 'b' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(22,12): error TS18046: 'a' is of type 'unknown'. @@ -24,9 +24,9 @@ unconstrainedTypeComparison.ts(45,16): error TS18048: 'b' is possibly 'undefined function f1(a: T, b: T): boolean { return a > b; ~ -!!! error TS18046: 'a' is of type 'unknown'. +!!! error TS18049: 'a' is possibly 'null' or 'undefined'. ~ -!!! error TS18046: 'b' is of type 'unknown'. +!!! error TS18049: 'b' is possibly 'null' or 'undefined'. } function f2(a: T, b: T): boolean { @@ -48,9 +48,9 @@ unconstrainedTypeComparison.ts(45,16): error TS18048: 'b' is possibly 'undefined function f4(a: U, b: U): boolean { return a > b; ~ -!!! error TS18046: 'a' is of type 'unknown'. +!!! error TS18049: 'a' is possibly 'null' or 'undefined'. ~ -!!! error TS18046: 'b' is of type 'unknown'. +!!! error TS18049: 'b' is possibly 'null' or 'undefined'. } function f5(a: U, b: U): boolean { From 0308ddefa12e063a59cfe3d4f95835c9479c3c86 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 22 Jul 2024 09:48:01 -0700 Subject: [PATCH 4/6] add intersections --- src/compiler/checker.ts | 21 ++--- .../unconstrainedTypeComparison.errors.txt | 40 ++++++++- .../unconstrainedTypeComparison.symbols | 82 ++++++++++++++++--- .../unconstrainedTypeComparison.types | 68 +++++++++++++++ .../compiler/unconstrainedTypeComparison.ts | 16 ++++ 5 files changed, 204 insertions(+), 23 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0a47b0e0f0ce5..b2e599e3f1d71 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24887,12 +24887,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // This like getBaseTypeOfLiteralType, but instead treats enum literals as strings/numbers instead // of returning their enum base type (which depends on the types of other literals in the enum). - function getBaseTypeOfLiteralTypeForComparison(type: Type): Type { - return type.flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ? stringType : + // use to determine if a parameter may be undefined or null (or is unknown/unconstrained) + // function getUnknownIfMaybeUnknown(type: Type) { + // return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) ?? unknownUnionType : type; + // } + function getBaseTypeForComparison(type: Type): Type { + return type.flags & TypeFlags.Instantiable && strictNullChecks ? getBaseConstraintOfType(type) ?? unknownUnionType : + type.flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ? stringType : type.flags & (TypeFlags.NumberLiteral | TypeFlags.Enum) ? numberType : type.flags & TypeFlags.BigIntLiteral ? bigintType : type.flags & TypeFlags.BooleanLiteral ? booleanType : - type.flags & TypeFlags.Union ? mapType(type, getBaseTypeOfLiteralTypeForComparison) : + type.flags & TypeFlags.Union ? mapType(type, getBaseTypeForComparison) : + type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => { return getBaseTypeForComparison(t) === unknownUnionType }) ? unknownUnionType : type; } @@ -27493,11 +27499,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type === unknownUnionType ? unknownType : type; } - // use to determine if a parameter may be undefined or null (or is unknown/unconstrained) - function getUnknownIfMaybeUnknown(type: Type) { - return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownUnionType : type; - } - function getTypeWithDefault(type: Type, defaultExpression: Expression) { return defaultExpression ? getUnionType([getNonUndefinedType(type), getTypeOfExpression(defaultExpression)]) : @@ -39682,8 +39683,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanEqualsToken: if (checkForDisallowedESSymbolOperand(operator)) { - leftType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(getUnknownIfMaybeUnknown(leftType), left)); - rightType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(getUnknownIfMaybeUnknown(rightType), right)); + leftType = checkNonNullType(getBaseTypeForComparison(leftType), left); + rightType = checkNonNullType(getBaseTypeForComparison(rightType), right); reportOperatorErrorUnless((left, right) => { if (isTypeAny(left) || isTypeAny(right)) { return true; diff --git a/tests/baselines/reference/unconstrainedTypeComparison.errors.txt b/tests/baselines/reference/unconstrainedTypeComparison.errors.txt index a2d250fdb0f48..d8f35367f49c7 100644 --- a/tests/baselines/reference/unconstrainedTypeComparison.errors.txt +++ b/tests/baselines/reference/unconstrainedTypeComparison.errors.txt @@ -16,11 +16,17 @@ unconstrainedTypeComparison.ts(30,12): error TS18047: 'a' is possibly 'null'. unconstrainedTypeComparison.ts(30,16): error TS18047: 'b' is possibly 'null'. unconstrainedTypeComparison.ts(34,12): error TS18049: 'a' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(34,16): error TS18049: 'b' is possibly 'null' or 'undefined'. -unconstrainedTypeComparison.ts(45,12): error TS18047: 'a' is possibly 'null'. -unconstrainedTypeComparison.ts(45,16): error TS18048: 'b' is possibly 'undefined'. +unconstrainedTypeComparison.ts(38,12): error TS18049: 'x' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(38,16): error TS18049: 'y' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(42,12): error TS18049: 'x' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(42,16): error TS18049: 'y' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(46,12): error TS18049: 'x' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(46,16): error TS18049: 'y' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(61,12): error TS18047: 'a' is possibly 'null'. +unconstrainedTypeComparison.ts(61,16): error TS18048: 'b' is possibly 'undefined'. -==== unconstrainedTypeComparison.ts (20 errors) ==== +==== unconstrainedTypeComparison.ts (26 errors) ==== function f1(a: T, b: T): boolean { return a > b; ~ @@ -93,6 +99,34 @@ unconstrainedTypeComparison.ts(45,16): error TS18048: 'b' is possibly 'undefined !!! error TS18049: 'b' is possibly 'null' or 'undefined'. } + function f10(x: T | U, y: T | U) { + return x < y; + ~ +!!! error TS18049: 'x' is possibly 'null' or 'undefined'. + ~ +!!! error TS18049: 'y' is possibly 'null' or 'undefined'. + } + + function f11(x: T | number, y: U | number) { + return x < y; + ~ +!!! error TS18049: 'x' is possibly 'null' or 'undefined'. + ~ +!!! error TS18049: 'y' is possibly 'null' or 'undefined'. + } + + function f12(x: T & U, y: T & U) { + return x < y; + ~ +!!! error TS18049: 'x' is possibly 'null' or 'undefined'. + ~ +!!! error TS18049: 'y' is possibly 'null' or 'undefined'. + } + + function f13(x: T & number, y: U & number) { + return x < y; + } + function compare(a: T, b: T): boolean { if (a === undefined) { diff --git a/tests/baselines/reference/unconstrainedTypeComparison.symbols b/tests/baselines/reference/unconstrainedTypeComparison.symbols index cc45524452a36..456f62b6f6a2d 100644 --- a/tests/baselines/reference/unconstrainedTypeComparison.symbols +++ b/tests/baselines/reference/unconstrainedTypeComparison.symbols @@ -130,27 +130,89 @@ function f9(a: U, b: U): boolean { >b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 32, 58)) } +function f10(x: T | U, y: T | U) { +>f10 : Symbol(f10, Decl(unconstrainedTypeComparison.ts, 34, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 36, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 36, 15)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 36, 19)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 36, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 36, 15)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 36, 28)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 36, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 36, 15)) + + return x < y; +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 36, 19)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 36, 28)) +} + +function f11(x: T | number, y: U | number) { +>f11 : Symbol(f11, Decl(unconstrainedTypeComparison.ts, 38, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 40, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 40, 15)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 40, 13)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 40, 29)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 40, 13)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 40, 43)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 40, 15)) + + return x < y; +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 40, 29)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 40, 43)) +} + +function f12(x: T & U, y: T & U) { +>f12 : Symbol(f12, Decl(unconstrainedTypeComparison.ts, 42, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 44, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 44, 15)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 44, 19)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 44, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 44, 15)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 44, 28)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 44, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 44, 15)) + + return x < y; +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 44, 19)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 44, 28)) +} + +function f13(x: T & number, y: U & number) { +>f13 : Symbol(f13, Decl(unconstrainedTypeComparison.ts, 46, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 48, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 48, 15)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 48, 13)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 48, 29)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 48, 13)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 48, 43)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 48, 15)) + + return x < y; +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 48, 29)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 48, 43)) +} + function compare(a: T, b: T): boolean { ->compare : Symbol(compare, Decl(unconstrainedTypeComparison.ts, 34, 1)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17)) ->a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17)) ->b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17)) +>compare : Symbol(compare, Decl(unconstrainedTypeComparison.ts, 50, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 53, 17)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 53, 20)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 53, 17)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 53, 25)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 53, 17)) if (a === undefined) { ->a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 53, 20)) >undefined : Symbol(undefined) return false; } if (b === null) { ->b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 53, 25)) return false; } return a > b; ->a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20)) ->b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 53, 20)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 53, 25)) } diff --git a/tests/baselines/reference/unconstrainedTypeComparison.types b/tests/baselines/reference/unconstrainedTypeComparison.types index b942d21aef957..5bea659ce52be 100644 --- a/tests/baselines/reference/unconstrainedTypeComparison.types +++ b/tests/baselines/reference/unconstrainedTypeComparison.types @@ -154,6 +154,74 @@ function f9(a: U, b: U): boolean { > : ^ } +function f10(x: T | U, y: T | U) { +>f10 : (x: T | U, y: T | U) => boolean +> : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ +>x : T | U +> : ^^^^^ +>y : T | U +> : ^^^^^ + + return x < y; +>x < y : boolean +> : ^^^^^^^ +>x : T | U +> : ^^^^^ +>y : T | U +> : ^^^^^ +} + +function f11(x: T | number, y: U | number) { +>f11 : (x: T | number, y: U | number) => boolean +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ +>x : number | T +> : ^^^^^^^^^^ +>y : number | U +> : ^^^^^^^^^^ + + return x < y; +>x < y : boolean +> : ^^^^^^^ +>x : number | T +> : ^^^^^^^^^^ +>y : number | U +> : ^^^^^^^^^^ +} + +function f12(x: T & U, y: T & U) { +>f12 : (x: T & U, y: T & U) => boolean +> : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ +>x : T & U +> : ^^^^^ +>y : T & U +> : ^^^^^ + + return x < y; +>x < y : boolean +> : ^^^^^^^ +>x : T & U +> : ^^^^^ +>y : T & U +> : ^^^^^ +} + +function f13(x: T & number, y: U & number) { +>f13 : (x: T & number, y: U & number) => boolean +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ +>x : T & number +> : ^^^^^^^^^^ +>y : U & number +> : ^^^^^^^^^^ + + return x < y; +>x < y : boolean +> : ^^^^^^^ +>x : T & number +> : ^^^^^^^^^^ +>y : U & number +> : ^^^^^^^^^^ +} + function compare(a: T, b: T): boolean { >compare : (a: T, b: T) => boolean diff --git a/tests/cases/compiler/unconstrainedTypeComparison.ts b/tests/cases/compiler/unconstrainedTypeComparison.ts index c2f197ff38fa5..84ec24d1a13de 100644 --- a/tests/cases/compiler/unconstrainedTypeComparison.ts +++ b/tests/cases/compiler/unconstrainedTypeComparison.ts @@ -38,6 +38,22 @@ function f9(a: U, b: U): boolean { return a > b; } +function f10(x: T | U, y: T | U) { + return x < y; +} + +function f11(x: T | number, y: U | number) { + return x < y; +} + +function f12(x: T & U, y: T & U) { + return x < y; +} + +function f13(x: T & number, y: U & number) { + return x < y; +} + function compare(a: T, b: T): boolean { if (a === undefined) { From 43c24bb40410651d3414c57f24d11d9d5e3a2994 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 22 Jul 2024 09:59:29 -0700 Subject: [PATCH 5/6] add tests, fix comment --- src/compiler/checker.ts | 5 +- .../unconstrainedTypeComparison.errors.txt | 56 +++++++- .../unconstrainedTypeComparison.symbols | 123 ++++++++++++++---- .../unconstrainedTypeComparison.types | 105 ++++++++++++++- .../compiler/unconstrainedTypeComparison.ts | 28 +++- 5 files changed, 273 insertions(+), 44 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b2e599e3f1d71..696d03b5c7535 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24887,10 +24887,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // This like getBaseTypeOfLiteralType, but instead treats enum literals as strings/numbers instead // of returning their enum base type (which depends on the types of other literals in the enum). - // use to determine if a parameter may be undefined or null (or is unknown/unconstrained) - // function getUnknownIfMaybeUnknown(type: Type) { - // return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) ?? unknownUnionType : type; - // } + // It also checks if the type used in the comparison may be undefined or null (or is unknown/unconstrained) function getBaseTypeForComparison(type: Type): Type { return type.flags & TypeFlags.Instantiable && strictNullChecks ? getBaseConstraintOfType(type) ?? unknownUnionType : type.flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ? stringType : diff --git a/tests/baselines/reference/unconstrainedTypeComparison.errors.txt b/tests/baselines/reference/unconstrainedTypeComparison.errors.txt index d8f35367f49c7..445c740f60d6a 100644 --- a/tests/baselines/reference/unconstrainedTypeComparison.errors.txt +++ b/tests/baselines/reference/unconstrainedTypeComparison.errors.txt @@ -20,13 +20,19 @@ unconstrainedTypeComparison.ts(38,12): error TS18049: 'x' is possibly 'null' or unconstrainedTypeComparison.ts(38,16): error TS18049: 'y' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(42,12): error TS18049: 'x' is possibly 'null' or 'undefined'. unconstrainedTypeComparison.ts(42,16): error TS18049: 'y' is possibly 'null' or 'undefined'. -unconstrainedTypeComparison.ts(46,12): error TS18049: 'x' is possibly 'null' or 'undefined'. -unconstrainedTypeComparison.ts(46,16): error TS18049: 'y' is possibly 'null' or 'undefined'. -unconstrainedTypeComparison.ts(61,12): error TS18047: 'a' is possibly 'null'. -unconstrainedTypeComparison.ts(61,16): error TS18048: 'b' is possibly 'undefined'. +unconstrainedTypeComparison.ts(49,12): error TS18047: 'x' is possibly 'null'. +unconstrainedTypeComparison.ts(49,16): error TS18049: 'y' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(56,12): error TS18047: 'x' is possibly 'null'. +unconstrainedTypeComparison.ts(56,16): error TS18049: 'y' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(60,12): error TS18049: 'x' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(60,16): error TS18049: 'y' is possibly 'null' or 'undefined'. +unconstrainedTypeComparison.ts(74,12): error TS18047: 'x' is possibly 'null'. +unconstrainedTypeComparison.ts(74,16): error TS18048: 'y' is possibly 'undefined'. +unconstrainedTypeComparison.ts(85,12): error TS18047: 'a' is possibly 'null'. +unconstrainedTypeComparison.ts(85,16): error TS18048: 'b' is possibly 'undefined'. -==== unconstrainedTypeComparison.ts (26 errors) ==== +==== unconstrainedTypeComparison.ts (32 errors) ==== function f1(a: T, b: T): boolean { return a > b; ~ @@ -115,7 +121,29 @@ unconstrainedTypeComparison.ts(61,16): error TS18048: 'b' is possibly 'undefined !!! error TS18049: 'y' is possibly 'null' or 'undefined'. } - function f12(x: T & U, y: T & U) { + function f12(x: T | number, y: U | number) { + if (x === undefined) { + return false; + } + return x < y; + ~ +!!! error TS18047: 'x' is possibly 'null'. + ~ +!!! error TS18049: 'y' is possibly 'null' or 'undefined'. + } + + function f13(x: U, y: U) { + if (x === undefined) { + return false; + } + return x < y; + ~ +!!! error TS18047: 'x' is possibly 'null'. + ~ +!!! error TS18049: 'y' is possibly 'null' or 'undefined'. + } + + function f14(x: T & U, y: T & U) { return x < y; ~ !!! error TS18049: 'x' is possibly 'null' or 'undefined'. @@ -123,8 +151,22 @@ unconstrainedTypeComparison.ts(61,16): error TS18048: 'b' is possibly 'undefined !!! error TS18049: 'y' is possibly 'null' or 'undefined'. } - function f13(x: T & number, y: U & number) { + function f15(x: T & number, y: U & number) { + return x < y; + } + + function f16(x: T & U, y: U) { + if (x === undefined) { + return false; + } + if (y === null) { + return false; + } return x < y; + ~ +!!! error TS18047: 'x' is possibly 'null'. + ~ +!!! error TS18048: 'y' is possibly 'undefined'. } diff --git a/tests/baselines/reference/unconstrainedTypeComparison.symbols b/tests/baselines/reference/unconstrainedTypeComparison.symbols index 456f62b6f6a2d..0a8a9a7a30d26 100644 --- a/tests/baselines/reference/unconstrainedTypeComparison.symbols +++ b/tests/baselines/reference/unconstrainedTypeComparison.symbols @@ -161,58 +161,127 @@ function f11(x: T | number, y: U | number) { >y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 40, 43)) } -function f12(x: T & U, y: T & U) { +function f12(x: T | number, y: U | number) { >f12 : Symbol(f12, Decl(unconstrainedTypeComparison.ts, 42, 1)) >T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 44, 13)) >U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 44, 15)) ->x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 44, 19)) >T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 44, 13)) ->U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 44, 15)) ->y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 44, 28)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 44, 29)) >T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 44, 13)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 44, 43)) >U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 44, 15)) + if (x === undefined) { +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 44, 29)) +>undefined : Symbol(undefined) + + return false; + } + return x < y; +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 44, 29)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 44, 43)) +} + +function f13(x: U, y: U) { +>f13 : Symbol(f13, Decl(unconstrainedTypeComparison.ts, 49, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 51, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 51, 15)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 51, 13)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 51, 38)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 51, 15)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 51, 43)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 51, 15)) + + if (x === undefined) { +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 51, 38)) +>undefined : Symbol(undefined) + + return false; + } + return x < y; +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 51, 38)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 51, 43)) +} + +function f14(x: T & U, y: T & U) { +>f14 : Symbol(f14, Decl(unconstrainedTypeComparison.ts, 56, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 58, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 58, 15)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 58, 19)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 58, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 58, 15)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 58, 28)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 58, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 58, 15)) + + return x < y; +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 58, 19)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 58, 28)) +} + +function f15(x: T & number, y: U & number) { +>f15 : Symbol(f15, Decl(unconstrainedTypeComparison.ts, 60, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 62, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 62, 15)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 62, 13)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 62, 29)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 62, 13)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 62, 43)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 62, 15)) + return x < y; ->x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 44, 19)) ->y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 44, 28)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 62, 29)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 62, 43)) } -function f13(x: T & number, y: U & number) { ->f13 : Symbol(f13, Decl(unconstrainedTypeComparison.ts, 46, 1)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 48, 13)) ->U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 48, 15)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 48, 13)) ->x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 48, 29)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 48, 13)) ->y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 48, 43)) ->U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 48, 15)) +function f16(x: T & U, y: U) { +>f16 : Symbol(f16, Decl(unconstrainedTypeComparison.ts, 64, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 66, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 66, 15)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 66, 13)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 66, 29)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 66, 13)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 66, 15)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 66, 38)) +>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 66, 15)) + if (x === undefined) { +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 66, 29)) +>undefined : Symbol(undefined) + + return false; + } + if (y === null) { +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 66, 38)) + + return false; + } return x < y; ->x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 48, 29)) ->y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 48, 43)) +>x : Symbol(x, Decl(unconstrainedTypeComparison.ts, 66, 29)) +>y : Symbol(y, Decl(unconstrainedTypeComparison.ts, 66, 38)) } function compare(a: T, b: T): boolean { ->compare : Symbol(compare, Decl(unconstrainedTypeComparison.ts, 50, 1)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 53, 17)) ->a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 53, 20)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 53, 17)) ->b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 53, 25)) ->T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 53, 17)) +>compare : Symbol(compare, Decl(unconstrainedTypeComparison.ts, 74, 1)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 77, 17)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 77, 20)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 77, 17)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 77, 25)) +>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 77, 17)) if (a === undefined) { ->a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 53, 20)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 77, 20)) >undefined : Symbol(undefined) return false; } if (b === null) { ->b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 53, 25)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 77, 25)) return false; } return a > b; ->a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 53, 20)) ->b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 53, 25)) +>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 77, 20)) +>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 77, 25)) } diff --git a/tests/baselines/reference/unconstrainedTypeComparison.types b/tests/baselines/reference/unconstrainedTypeComparison.types index 5bea659ce52be..8f3808ddb7cad 100644 --- a/tests/baselines/reference/unconstrainedTypeComparison.types +++ b/tests/baselines/reference/unconstrainedTypeComparison.types @@ -188,8 +188,66 @@ function f11(x: T | number, y: U | number) { > : ^^^^^^^^^^ } -function f12(x: T & U, y: T & U) { ->f12 : (x: T & U, y: T & U) => boolean +function f12(x: T | number, y: U | number) { +>f12 : (x: T | number, y: U | number) => boolean +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ +>x : number | T +> : ^^^^^^^^^^ +>y : number | U +> : ^^^^^^^^^^ + + if (x === undefined) { +>x === undefined : boolean +> : ^^^^^^^ +>x : number | T +> : ^^^^^^^^^^ +>undefined : undefined +> : ^^^^^^^^^ + + return false; +>false : false +> : ^^^^^ + } + return x < y; +>x < y : boolean +> : ^^^^^^^ +>x : number | (T & ({} | null)) +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>y : number | U +> : ^^^^^^^^^^ +} + +function f13(x: U, y: U) { +>f13 : (x: U, y: U) => boolean +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ +>x : U +> : ^ +>y : U +> : ^ + + if (x === undefined) { +>x === undefined : boolean +> : ^^^^^^^ +>x : U +> : ^ +>undefined : undefined +> : ^^^^^^^^^ + + return false; +>false : false +> : ^^^^^ + } + return x < y; +>x < y : boolean +> : ^^^^^^^ +>x : U & ({} | null) +> : ^^^^^^^^^^^^^^^ +>y : U +> : ^ +} + +function f14(x: T & U, y: T & U) { +>f14 : (x: T & U, y: T & U) => boolean > : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ >x : T & U > : ^^^^^ @@ -205,8 +263,8 @@ function f12(x: T & U, y: T & U) { > : ^^^^^ } -function f13(x: T & number, y: U & number) { ->f13 : (x: T & number, y: U & number) => boolean +function f15(x: T & number, y: U & number) { +>f15 : (x: T & number, y: U & number) => boolean > : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ >x : T & number > : ^^^^^^^^^^ @@ -222,6 +280,45 @@ function f13(x: T & number, y: U & number) { > : ^^^^^^^^^^ } +function f16(x: T & U, y: U) { +>f16 : (x: T & U, y: U) => boolean +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^ +>x : T & U +> : ^^^^^ +>y : U +> : ^ + + if (x === undefined) { +>x === undefined : boolean +> : ^^^^^^^ +>x : T & U +> : ^^^^^ +>undefined : undefined +> : ^^^^^^^^^ + + return false; +>false : false +> : ^^^^^ + } + if (y === null) { +>y === null : boolean +> : ^^^^^^^ +>y : U +> : ^ + + return false; +>false : false +> : ^^^^^ + } + return x < y; +>x < y : boolean +> : ^^^^^^^ +>x : T & U & ({} | null) +> : ^^^^^^^^^^^^^^^^^^^ +>y : U & ({} | undefined) +> : ^^^^^^^^^^^^^^^^^^^^ +} + function compare(a: T, b: T): boolean { >compare : (a: T, b: T) => boolean diff --git a/tests/cases/compiler/unconstrainedTypeComparison.ts b/tests/cases/compiler/unconstrainedTypeComparison.ts index 84ec24d1a13de..ad69988741917 100644 --- a/tests/cases/compiler/unconstrainedTypeComparison.ts +++ b/tests/cases/compiler/unconstrainedTypeComparison.ts @@ -46,11 +46,35 @@ function f11(x: T | number, y: U | number) { return x < y; } -function f12(x: T & U, y: T & U) { +function f12(x: T | number, y: U | number) { + if (x === undefined) { + return false; + } return x < y; } -function f13(x: T & number, y: U & number) { +function f13(x: U, y: U) { + if (x === undefined) { + return false; + } + return x < y; +} + +function f14(x: T & U, y: T & U) { + return x < y; +} + +function f15(x: T & number, y: U & number) { + return x < y; +} + +function f16(x: T & U, y: U) { + if (x === undefined) { + return false; + } + if (y === null) { + return false; + } return x < y; } From 529331b7477a0bdac5bbd47dda63e58180381178 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 22 Jul 2024 10:08:46 -0700 Subject: [PATCH 6/6] format --- src/compiler/checker.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 696d03b5c7535..25a34b894cd59 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24895,7 +24895,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type.flags & TypeFlags.BigIntLiteral ? bigintType : type.flags & TypeFlags.BooleanLiteral ? booleanType : type.flags & TypeFlags.Union ? mapType(type, getBaseTypeForComparison) : - type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => { return getBaseTypeForComparison(t) === unknownUnionType }) ? unknownUnionType : + type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => { + return getBaseTypeForComparison(t) === unknownUnionType; + }) ? unknownUnionType : type; }