diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad6e72d68b..93b644b07a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6356,9 +6356,6 @@ namespace ts { return type; function narrowTypeByEquality(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - if (!(type.flags & TypeFlags.Union)) { - return type; - } // Check that we have 'typeof ' on the left and string literal on the right if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) { return type; @@ -6372,6 +6369,14 @@ namespace ts { assumeTrue = !assumeTrue; } let typeInfo = primitiveTypeInfo[right.text]; + // If the type to be narrowed is any and we're affirmatively checking against a primitive, return the primitive + if (!!(type.flags & TypeFlags.Any) && typeInfo && assumeTrue) { + return typeInfo.type; + } + // At this point we can bail if it's not a union + if (!(type.flags & TypeFlags.Union)) { + return type; + } let flags = typeInfo ? typeInfo.flags : (assumeTrue = !assumeTrue, TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol | TypeFlags.Boolean); let union = type as UnionType; if (assumeTrue) { diff --git a/tests/baselines/reference/typeGuardsWithAny.errors.txt b/tests/baselines/reference/typeGuardsWithAny.errors.txt new file mode 100644 index 0000000000..653c89c755 --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithAny.errors.txt @@ -0,0 +1,49 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(11,7): error TS2339: Property 'p' does not exist on type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(18,7): error TS2339: Property 'p' does not exist on type 'number'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(25,7): error TS2339: Property 'p' does not exist on type 'boolean'. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts (3 errors) ==== + var x: any = { p: 0 }; + + if (x instanceof Object) { + x.p; // No error, type any unaffected by instanceof type guard + } + else { + x.p; // No error, type any unaffected by instanceof type guard + } + + if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'string'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'number'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'boolean'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check + } + else { + x.p; // No error, type unaffected in this branch + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithAny.symbols b/tests/baselines/reference/typeGuardsWithAny.symbols deleted file mode 100644 index 90405d762a..0000000000 --- a/tests/baselines/reference/typeGuardsWithAny.symbols +++ /dev/null @@ -1,61 +0,0 @@ -=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts === -var x: any = { p: 0 }; ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) ->p : Symbol(p, Decl(typeGuardsWithAny.ts, 0, 14)) - -if (x instanceof Object) { ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) ->Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) - - x.p; // No error, type any unaffected by instanceof type guard ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} -else { - x.p; // No error, type any unaffected by instanceof type guard ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} - -if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) - - x.p; // Error, type any narrowed by primitive type check ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} -else { - x.p; // No error, type unaffected in this branch ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} - -if (typeof x === "number") { ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) - - x.p; // Error, type any narrowed by primitive type check ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} -else { - x.p; // No error, type unaffected in this branch ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} - -if (typeof x === "boolean") { ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) - - x.p; // Error, type any narrowed by primitive type check ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} -else { - x.p; // No error, type unaffected in this branch ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} - -if (typeof x === "object") { ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) - - x.p; // No error, type any only affected by primitive type check ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} -else { - x.p; // No error, type unaffected in this branch ->x : Symbol(x, Decl(typeGuardsWithAny.ts, 0, 3)) -} - diff --git a/tests/baselines/reference/typeGuardsWithAny.types b/tests/baselines/reference/typeGuardsWithAny.types deleted file mode 100644 index 12a4326e99..0000000000 --- a/tests/baselines/reference/typeGuardsWithAny.types +++ /dev/null @@ -1,96 +0,0 @@ -=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts === -var x: any = { p: 0 }; ->x : any ->{ p: 0 } : { p: number; } ->p : number ->0 : number - -if (x instanceof Object) { ->x instanceof Object : boolean ->x : any ->Object : ObjectConstructor - - x.p; // No error, type any unaffected by instanceof type guard ->x.p : any ->x : any ->p : any -} -else { - x.p; // No error, type any unaffected by instanceof type guard ->x.p : any ->x : any ->p : any -} - -if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : any ->"string" : string - - x.p; // Error, type any narrowed by primitive type check ->x.p : any ->x : any ->p : any -} -else { - x.p; // No error, type unaffected in this branch ->x.p : any ->x : any ->p : any -} - -if (typeof x === "number") { ->typeof x === "number" : boolean ->typeof x : string ->x : any ->"number" : string - - x.p; // Error, type any narrowed by primitive type check ->x.p : any ->x : any ->p : any -} -else { - x.p; // No error, type unaffected in this branch ->x.p : any ->x : any ->p : any -} - -if (typeof x === "boolean") { ->typeof x === "boolean" : boolean ->typeof x : string ->x : any ->"boolean" : string - - x.p; // Error, type any narrowed by primitive type check ->x.p : any ->x : any ->p : any -} -else { - x.p; // No error, type unaffected in this branch ->x.p : any ->x : any ->p : any -} - -if (typeof x === "object") { ->typeof x === "object" : boolean ->typeof x : string ->x : any ->"object" : string - - x.p; // No error, type any only affected by primitive type check ->x.p : any ->x : any ->p : any -} -else { - x.p; // No error, type unaffected in this branch ->x.p : any ->x : any ->p : any -} -