From 72da4b8f127b62f064f1a2acf38a104cb333cf6c Mon Sep 17 00:00:00 2001 From: Charles Pierce Date: Tue, 28 Nov 2017 15:05:06 -0800 Subject: [PATCH] Error when LHS of instanceof is Union of Primitives #18519 (#19063) * Error when LHS of instanceof is Union of Primitives #18519 * Refactor to allTypesAssignableToKind method and update test * Use ternary expression instead of if / else blocks --- src/compiler/checker.ts | 9 +++++- .../instanceofWithPrimitiveUnion.errors.txt | 21 ++++++++++++++ .../reference/instanceofWithPrimitiveUnion.js | 25 ++++++++++++++++ .../instanceofWithPrimitiveUnion.symbols | 27 +++++++++++++++++ .../instanceofWithPrimitiveUnion.types | 29 +++++++++++++++++++ .../compiler/instanceofWithPrimitiveUnion.ts | 11 +++++++ 6 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/instanceofWithPrimitiveUnion.errors.txt create mode 100644 tests/baselines/reference/instanceofWithPrimitiveUnion.js create mode 100644 tests/baselines/reference/instanceofWithPrimitiveUnion.symbols create mode 100644 tests/baselines/reference/instanceofWithPrimitiveUnion.types create mode 100644 tests/cases/compiler/instanceofWithPrimitiveUnion.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bcf95543d3..bcb84a4814 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18314,6 +18314,12 @@ namespace ts { (kind & TypeFlags.NonPrimitive && isTypeAssignableTo(source, nonPrimitiveType)); } + function allTypesAssignableToKind(source: Type, kind: TypeFlags, strict?: boolean): boolean { + return source.flags & TypeFlags.Union ? + every((source as UnionType).types, subType => allTypesAssignableToKind(subType, kind, strict)) : + isTypeAssignableToKind(source, kind, strict); + } + function isConstEnumObjectType(type: Type): boolean { return getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && isConstEnumSymbol(type.symbol); } @@ -18331,7 +18337,8 @@ namespace ts { // and the right operand to be of type Any, a subtype of the 'Function' interface type, or have a call or construct signature. // The result is always of the Boolean primitive type. // NOTE: do not raise error if leftType is unknown as related error was already reported - if (!isTypeAny(leftType) && isTypeAssignableToKind(leftType, TypeFlags.Primitive)) { + if (!isTypeAny(leftType) && + allTypesAssignableToKind(leftType, TypeFlags.Primitive)) { error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } // NOTE: do not raise error if right is unknown as related error was already reported diff --git a/tests/baselines/reference/instanceofWithPrimitiveUnion.errors.txt b/tests/baselines/reference/instanceofWithPrimitiveUnion.errors.txt new file mode 100644 index 0000000000..0172b89fec --- /dev/null +++ b/tests/baselines/reference/instanceofWithPrimitiveUnion.errors.txt @@ -0,0 +1,21 @@ +tests/cases/compiler/instanceofWithPrimitiveUnion.ts(2,9): error TS2358: The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter. +tests/cases/compiler/instanceofWithPrimitiveUnion.ts(8,9): error TS2358: The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter. + + +==== tests/cases/compiler/instanceofWithPrimitiveUnion.ts (2 errors) ==== + function test1(x: number | string) { + if (x instanceof Object) { + ~ +!!! error TS2358: The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter. + x; + } + } + + function test2(x: (number | string) | number) { + if (x instanceof Object) { + ~ +!!! error TS2358: The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter. + x; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/instanceofWithPrimitiveUnion.js b/tests/baselines/reference/instanceofWithPrimitiveUnion.js new file mode 100644 index 0000000000..5510f893f1 --- /dev/null +++ b/tests/baselines/reference/instanceofWithPrimitiveUnion.js @@ -0,0 +1,25 @@ +//// [instanceofWithPrimitiveUnion.ts] +function test1(x: number | string) { + if (x instanceof Object) { + x; + } +} + +function test2(x: (number | string) | number) { + if (x instanceof Object) { + x; + } +} + + +//// [instanceofWithPrimitiveUnion.js] +function test1(x) { + if (x instanceof Object) { + x; + } +} +function test2(x) { + if (x instanceof Object) { + x; + } +} diff --git a/tests/baselines/reference/instanceofWithPrimitiveUnion.symbols b/tests/baselines/reference/instanceofWithPrimitiveUnion.symbols new file mode 100644 index 0000000000..f9ad5ac943 --- /dev/null +++ b/tests/baselines/reference/instanceofWithPrimitiveUnion.symbols @@ -0,0 +1,27 @@ +=== tests/cases/compiler/instanceofWithPrimitiveUnion.ts === +function test1(x: number | string) { +>test1 : Symbol(test1, Decl(instanceofWithPrimitiveUnion.ts, 0, 0)) +>x : Symbol(x, Decl(instanceofWithPrimitiveUnion.ts, 0, 15)) + + if (x instanceof Object) { +>x : Symbol(x, Decl(instanceofWithPrimitiveUnion.ts, 0, 15)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + x; +>x : Symbol(x, Decl(instanceofWithPrimitiveUnion.ts, 0, 15)) + } +} + +function test2(x: (number | string) | number) { +>test2 : Symbol(test2, Decl(instanceofWithPrimitiveUnion.ts, 4, 1)) +>x : Symbol(x, Decl(instanceofWithPrimitiveUnion.ts, 6, 15)) + + if (x instanceof Object) { +>x : Symbol(x, Decl(instanceofWithPrimitiveUnion.ts, 6, 15)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + x; +>x : Symbol(x, Decl(instanceofWithPrimitiveUnion.ts, 6, 15)) + } +} + diff --git a/tests/baselines/reference/instanceofWithPrimitiveUnion.types b/tests/baselines/reference/instanceofWithPrimitiveUnion.types new file mode 100644 index 0000000000..fcaba05b51 --- /dev/null +++ b/tests/baselines/reference/instanceofWithPrimitiveUnion.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/instanceofWithPrimitiveUnion.ts === +function test1(x: number | string) { +>test1 : (x: string | number) => void +>x : string | number + + if (x instanceof Object) { +>x instanceof Object : boolean +>x : string | number +>Object : ObjectConstructor + + x; +>x : string | number + } +} + +function test2(x: (number | string) | number) { +>test2 : (x: string | number) => void +>x : string | number + + if (x instanceof Object) { +>x instanceof Object : boolean +>x : string | number +>Object : ObjectConstructor + + x; +>x : string | number + } +} + diff --git a/tests/cases/compiler/instanceofWithPrimitiveUnion.ts b/tests/cases/compiler/instanceofWithPrimitiveUnion.ts new file mode 100644 index 0000000000..ef70aa2602 --- /dev/null +++ b/tests/cases/compiler/instanceofWithPrimitiveUnion.ts @@ -0,0 +1,11 @@ +function test1(x: number | string) { + if (x instanceof Object) { + x; + } +} + +function test2(x: (number | string) | number) { + if (x instanceof Object) { + x; + } +}