From febda00f1b430761df7e0e90bbb99cf8c99e4653 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 28 Oct 2015 16:06:40 -0700 Subject: [PATCH] Improve type narrowing algorithm with typeof --- src/compiler/checker.ts | 35 +++---- tests/baselines/reference/symbolType18.types | 2 +- tests/baselines/reference/typeGuardNesting.js | 11 +++ .../reference/typeGuardNesting.symbols | 14 +++ .../reference/typeGuardNesting.types | 30 ++++++ .../typeGuardOfFormTypeOfOther.types | 12 +-- .../reference/typeGuardRedundancy.js | 17 ++++ .../reference/typeGuardRedundancy.symbols | 48 ++++++++++ .../reference/typeGuardRedundancy.types | 84 ++++++++++++++++ .../reference/typeGuardsWithAny.errors.txt | 49 ---------- .../reference/typeGuardsWithAny.symbols | 61 ++++++++++++ .../reference/typeGuardsWithAny.types | 96 +++++++++++++++++++ 12 files changed, 382 insertions(+), 77 deletions(-) create mode 100644 tests/baselines/reference/typeGuardNesting.js create mode 100644 tests/baselines/reference/typeGuardNesting.symbols create mode 100644 tests/baselines/reference/typeGuardNesting.types create mode 100644 tests/baselines/reference/typeGuardRedundancy.js create mode 100644 tests/baselines/reference/typeGuardRedundancy.symbols create mode 100644 tests/baselines/reference/typeGuardRedundancy.types delete mode 100644 tests/baselines/reference/typeGuardsWithAny.errors.txt create mode 100644 tests/baselines/reference/typeGuardsWithAny.symbols create mode 100644 tests/baselines/reference/typeGuardsWithAny.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 033614d2b41..ad6e72d68bd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6338,6 +6338,10 @@ namespace ts { // Stop at the first containing function or module declaration break loop; } + // Preserve old top-level behavior - if the branch is really an empty set, revert to prior type + if (narrowedType === getUnionType(emptyArray)) { + narrowedType = type; + } // Use narrowed type if construct contains no assignments to variable if (narrowedType !== type) { if (isVariableAssignedWithin(symbol, node)) { @@ -6352,6 +6356,9 @@ 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; @@ -6361,31 +6368,17 @@ namespace ts { if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(left.expression) !== symbol) { return type; } - let typeInfo = primitiveTypeInfo[right.text]; if (expr.operatorToken.kind === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } + let typeInfo = primitiveTypeInfo[right.text]; + let flags = typeInfo ? typeInfo.flags : (assumeTrue = !assumeTrue, TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol | TypeFlags.Boolean); + let union = type as UnionType; if (assumeTrue) { - // Assumed result is true. If check was not for a primitive type, remove all primitive types - if (!typeInfo) { - return removeTypesFromUnionType(type, /*typeKind*/ TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.Boolean | TypeFlags.ESSymbol, - /*isOfTypeKind*/ true, /*allowEmptyUnionResult*/ true); - } - // Check was for a primitive type, return that primitive type if it is a subtype - if (isTypeSubtypeOf(typeInfo.type, type)) { - return typeInfo.type; - } - // Otherwise, remove all types that aren't of the primitive type kind. This can happen when the type is - // union of enum types and other types. - return removeTypesFromUnionType(type, /*typeKind*/ typeInfo.flags, /*isOfTypeKind*/ false, /*allowEmptyUnionResult*/ true); + return getUnionType(filter(union.types, t => !!(t.flags & flags))); } else { - // Assumed result is false. If check was for a primitive type, remove that primitive type - if (typeInfo) { - return removeTypesFromUnionType(type, /*typeKind*/ typeInfo.flags, /*isOfTypeKind*/ true, /*allowEmptyUnionResult*/ true); - } - // Otherwise we don't have enough information to do anything. - return type; + return getUnionType(filter(union.types, t => !(t.flags & flags))); } } @@ -6399,7 +6392,7 @@ namespace ts { // and the second operand was false. We narrow with those assumptions and union the two resulting types. return getUnionType([ narrowType(type, expr.left, /*assumeTrue*/ false), - narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ false) + narrowType(type, expr.right, /*assumeTrue*/ false) ]); } } @@ -6410,7 +6403,7 @@ namespace ts { // and the second operand was true. We narrow with those assumptions and union the two resulting types. return getUnionType([ narrowType(type, expr.left, /*assumeTrue*/ true), - narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ true) + narrowType(type, expr.right, /*assumeTrue*/ true) ]); } else { diff --git a/tests/baselines/reference/symbolType18.types b/tests/baselines/reference/symbolType18.types index db4fb30378f..68c43215136 100644 --- a/tests/baselines/reference/symbolType18.types +++ b/tests/baselines/reference/symbolType18.types @@ -21,5 +21,5 @@ if (typeof x === "object") { } else { x; ->x : symbol | Foo +>x : symbol } diff --git a/tests/baselines/reference/typeGuardNesting.js b/tests/baselines/reference/typeGuardNesting.js new file mode 100644 index 00000000000..4c9a64c2851 --- /dev/null +++ b/tests/baselines/reference/typeGuardNesting.js @@ -0,0 +1,11 @@ +//// [typeGuardNesting.ts] +let strOrBool: string|boolean; +if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'string') { + var label: string = (typeof strOrBool === 'string') ? strOrBool : "other string"; +} + +//// [typeGuardNesting.js] +var strOrBool; +if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'string') { + var label = (typeof strOrBool === 'string') ? strOrBool : "other string"; +} diff --git a/tests/baselines/reference/typeGuardNesting.symbols b/tests/baselines/reference/typeGuardNesting.symbols new file mode 100644 index 00000000000..f0cfb1f1eed --- /dev/null +++ b/tests/baselines/reference/typeGuardNesting.symbols @@ -0,0 +1,14 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNesting.ts === +let strOrBool: string|boolean; +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) + +if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'string') { +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) + + var label: string = (typeof strOrBool === 'string') ? strOrBool : "other string"; +>label : Symbol(label, Decl(typeGuardNesting.ts, 2, 4)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +} diff --git a/tests/baselines/reference/typeGuardNesting.types b/tests/baselines/reference/typeGuardNesting.types new file mode 100644 index 00000000000..9f79974635d --- /dev/null +++ b/tests/baselines/reference/typeGuardNesting.types @@ -0,0 +1,30 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNesting.ts === +let strOrBool: string|boolean; +>strOrBool : string | boolean + +if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'string') { +>(typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'string' : boolean +>(typeof strOrBool === 'boolean' && !strOrBool) : boolean +>typeof strOrBool === 'boolean' && !strOrBool : boolean +>typeof strOrBool === 'boolean' : boolean +>typeof strOrBool : string +>strOrBool : string | boolean +>'boolean' : string +>!strOrBool : boolean +>strOrBool : boolean +>typeof strOrBool === 'string' : boolean +>typeof strOrBool : string +>strOrBool : string | boolean +>'string' : string + + var label: string = (typeof strOrBool === 'string') ? strOrBool : "other string"; +>label : string +>(typeof strOrBool === 'string') ? strOrBool : "other string" : string +>(typeof strOrBool === 'string') : boolean +>typeof strOrBool === 'string' : boolean +>typeof strOrBool : string +>strOrBool : boolean | string +>'string' : string +>strOrBool : string +>"other string" : string +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.types b/tests/baselines/reference/typeGuardOfFormTypeOfOther.types index e0fd443ef63..8150c3ec25d 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfOther.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.types @@ -63,7 +63,7 @@ else { var r2: string | C = strOrC; // string | C >r2 : string | C >C : C ->strOrC : string | C +>strOrC : string } if (typeof numOrC === "Object") { >typeof numOrC === "Object" : boolean @@ -80,7 +80,7 @@ else { var r3: number | C = numOrC; // number | C >r3 : number | C >C : C ->numOrC : number | C +>numOrC : number } if (typeof boolOrC === "Object") { >typeof boolOrC === "Object" : boolean @@ -97,7 +97,7 @@ else { var r4: boolean | C = boolOrC; // boolean | C >r4 : boolean | C >C : C ->boolOrC : boolean | C +>boolOrC : boolean } // Narrowing occurs only if target type is a subtype of variable type @@ -129,7 +129,7 @@ if (typeof strOrC !== "Object") { var r2: string | C = strOrC; // string | C >r2 : string | C >C : C ->strOrC : string | C +>strOrC : string } else { c = strOrC; // C @@ -146,7 +146,7 @@ if (typeof numOrC !== "Object") { var r3: number | C = numOrC; // number | C >r3 : number | C >C : C ->numOrC : number | C +>numOrC : number } else { c = numOrC; // C @@ -163,7 +163,7 @@ if (typeof boolOrC !== "Object") { var r4: boolean | C = boolOrC; // boolean | C >r4 : boolean | C >C : C ->boolOrC : boolean | C +>boolOrC : boolean } else { c = boolOrC; // C diff --git a/tests/baselines/reference/typeGuardRedundancy.js b/tests/baselines/reference/typeGuardRedundancy.js new file mode 100644 index 00000000000..1dfa964cbc9 --- /dev/null +++ b/tests/baselines/reference/typeGuardRedundancy.js @@ -0,0 +1,17 @@ +//// [typeGuardRedundancy.ts] +var x: string|number; + +var r1 = typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed; + +var r2 = !(typeof x === "string" && typeof x === "string") ? x.toFixed : x.substr; + +var r3 = typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed; + +var r4 = !(typeof x === "string" || typeof x === "string") ? x.toFixed : x.substr; + +//// [typeGuardRedundancy.js] +var x; +var r1 = typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed; +var r2 = !(typeof x === "string" && typeof x === "string") ? x.toFixed : x.substr; +var r3 = typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed; +var r4 = !(typeof x === "string" || typeof x === "string") ? x.toFixed : x.substr; diff --git a/tests/baselines/reference/typeGuardRedundancy.symbols b/tests/baselines/reference/typeGuardRedundancy.symbols new file mode 100644 index 00000000000..356061407ad --- /dev/null +++ b/tests/baselines/reference/typeGuardRedundancy.symbols @@ -0,0 +1,48 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardRedundancy.ts === +var x: string|number; +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) + +var r1 = typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed; +>r1 : Symbol(r1, Decl(typeGuardRedundancy.ts, 2, 3)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>x.substr : Symbol(String.substr, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>substr : Symbol(String.substr, Decl(lib.d.ts, --, --)) +>x.toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) + +var r2 = !(typeof x === "string" && typeof x === "string") ? x.toFixed : x.substr; +>r2 : Symbol(r2, Decl(typeGuardRedundancy.ts, 4, 3)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>x.toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) +>x.substr : Symbol(String.substr, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>substr : Symbol(String.substr, Decl(lib.d.ts, --, --)) + +var r3 = typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed; +>r3 : Symbol(r3, Decl(typeGuardRedundancy.ts, 6, 3)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>x.substr : Symbol(String.substr, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>substr : Symbol(String.substr, Decl(lib.d.ts, --, --)) +>x.toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) + +var r4 = !(typeof x === "string" || typeof x === "string") ? x.toFixed : x.substr; +>r4 : Symbol(r4, Decl(typeGuardRedundancy.ts, 8, 3)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>x.toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) +>x.substr : Symbol(String.substr, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardRedundancy.ts, 0, 3)) +>substr : Symbol(String.substr, Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/typeGuardRedundancy.types b/tests/baselines/reference/typeGuardRedundancy.types new file mode 100644 index 00000000000..1507ceb850e --- /dev/null +++ b/tests/baselines/reference/typeGuardRedundancy.types @@ -0,0 +1,84 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardRedundancy.ts === +var x: string|number; +>x : string | number + +var r1 = typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed; +>r1 : (from: number, length?: number) => string +>typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed : (from: number, length?: number) => string +>typeof x === "string" && typeof x === "string" : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string +>typeof x === "string" : boolean +>typeof x : string +>x : string +>"string" : string +>x.substr : (from: number, length?: number) => string +>x : string +>substr : (from: number, length?: number) => string +>x.toFixed : (fractionDigits?: number) => string +>x : number +>toFixed : (fractionDigits?: number) => string + +var r2 = !(typeof x === "string" && typeof x === "string") ? x.toFixed : x.substr; +>r2 : (fractionDigits?: number) => string +>!(typeof x === "string" && typeof x === "string") ? x.toFixed : x.substr : (fractionDigits?: number) => string +>!(typeof x === "string" && typeof x === "string") : boolean +>(typeof x === "string" && typeof x === "string") : boolean +>typeof x === "string" && typeof x === "string" : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string +>typeof x === "string" : boolean +>typeof x : string +>x : string +>"string" : string +>x.toFixed : (fractionDigits?: number) => string +>x : number +>toFixed : (fractionDigits?: number) => string +>x.substr : (from: number, length?: number) => string +>x : string +>substr : (from: number, length?: number) => string + +var r3 = typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed; +>r3 : (from: number, length?: number) => string +>typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed : (from: number, length?: number) => string +>typeof x === "string" || typeof x === "string" : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string +>typeof x === "string" : boolean +>typeof x : string +>x : number +>"string" : string +>x.substr : (from: number, length?: number) => string +>x : string +>substr : (from: number, length?: number) => string +>x.toFixed : (fractionDigits?: number) => string +>x : number +>toFixed : (fractionDigits?: number) => string + +var r4 = !(typeof x === "string" || typeof x === "string") ? x.toFixed : x.substr; +>r4 : (fractionDigits?: number) => string +>!(typeof x === "string" || typeof x === "string") ? x.toFixed : x.substr : (fractionDigits?: number) => string +>!(typeof x === "string" || typeof x === "string") : boolean +>(typeof x === "string" || typeof x === "string") : boolean +>typeof x === "string" || typeof x === "string" : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string +>typeof x === "string" : boolean +>typeof x : string +>x : number +>"string" : string +>x.toFixed : (fractionDigits?: number) => string +>x : number +>toFixed : (fractionDigits?: number) => string +>x.substr : (from: number, length?: number) => string +>x : string +>substr : (from: number, length?: number) => string + diff --git a/tests/baselines/reference/typeGuardsWithAny.errors.txt b/tests/baselines/reference/typeGuardsWithAny.errors.txt deleted file mode 100644 index 653c89c7554..00000000000 --- a/tests/baselines/reference/typeGuardsWithAny.errors.txt +++ /dev/null @@ -1,49 +0,0 @@ -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 new file mode 100644 index 00000000000..90405d762a1 --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithAny.symbols @@ -0,0 +1,61 @@ +=== 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 new file mode 100644 index 00000000000..12a4326e99d --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithAny.types @@ -0,0 +1,96 @@ +=== 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 +} +