From 9f12bc8a1b0d100e87d336d8303fe5cf9fe09045 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 29 Oct 2015 17:51:36 -0700 Subject: [PATCH] feedback from pr, new tests --- src/compiler/checker.ts | 61 +++++------ tests/baselines/reference/typeGuardEnums.js | 41 +++++++ .../reference/typeGuardEnums.symbols | 34 ++++++ .../baselines/reference/typeGuardEnums.types | 40 +++++++ tests/baselines/reference/typeGuardNesting.js | 26 ++++- .../reference/typeGuardNesting.symbols | 44 +++++++- .../reference/typeGuardNesting.types | 100 +++++++++++++++++- .../reference/typeGuardOfFormTypeOfOther.js | 24 ++--- .../typeGuardOfFormTypeOfOther.symbols | 18 ++-- .../typeGuardOfFormTypeOfOther.types | 30 +++--- .../typeGuardTautologicalConsistiency.js | 24 +++++ .../typeGuardTautologicalConsistiency.symbols | 23 ++++ .../typeGuardTautologicalConsistiency.types | 36 +++++++ .../expressions/typeGuards/typeGuardEnums.ts | 18 ++++ .../typeGuards/typeGuardNesting.ts | 14 ++- .../typeGuards/typeGuardOfFormTypeOfOther.ts | 12 +-- .../typeGuardTautologicalConsistiency.ts | 11 ++ 17 files changed, 463 insertions(+), 93 deletions(-) create mode 100644 tests/baselines/reference/typeGuardEnums.js create mode 100644 tests/baselines/reference/typeGuardEnums.symbols create mode 100644 tests/baselines/reference/typeGuardEnums.types create mode 100644 tests/baselines/reference/typeGuardTautologicalConsistiency.js create mode 100644 tests/baselines/reference/typeGuardTautologicalConsistiency.symbols create mode 100644 tests/baselines/reference/typeGuardTautologicalConsistiency.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardEnums.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardTautologicalConsistiency.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 93b644b07a4..4e8cb7cc340 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6177,27 +6177,6 @@ namespace ts { Debug.fail("should not get here"); } - // For a union type, remove all constituent types that are of the given type kind (when isOfTypeKind is true) - // or not of the given type kind (when isOfTypeKind is false) - function removeTypesFromUnionType(type: Type, typeKind: TypeFlags, isOfTypeKind: boolean, allowEmptyUnionResult: boolean): Type { - if (type.flags & TypeFlags.Union) { - let types = (type).types; - if (forEach(types, t => !!(t.flags & typeKind) === isOfTypeKind)) { - // Above we checked if we have anything to remove, now use the opposite test to do the removal - let narrowedType = getUnionType(filter(types, t => !(t.flags & typeKind) === isOfTypeKind)); - if (allowEmptyUnionResult || narrowedType !== emptyObjectType) { - return narrowedType; - } - } - } - else if (allowEmptyUnionResult && !!(type.flags & typeKind) === isOfTypeKind) { - // Use getUnionType(emptyArray) instead of emptyObjectType in case the way empty union types - // are represented ever changes. - return getUnionType(emptyArray); - } - return type; - } - function hasInitializer(node: VariableLikeDeclaration): boolean { return !!(node.initializer || isBindingPattern(node.parent) && hasInitializer(node.parent.parent)); } @@ -6296,6 +6275,7 @@ namespace ts { // Get the narrowed type of a given symbol at a given location function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) { let type = getTypeOfSymbol(symbol); + let originalType = type; // Only narrow when symbol is variable of type any or an object, union, or type parameter type if (node && symbol.flags & SymbolFlags.Variable) { if (isTypeAny(type) || type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { @@ -6338,10 +6318,6 @@ 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)) { @@ -6350,6 +6326,11 @@ namespace ts { type = narrowedType; } } + + // Preserve old top-level behavior - if the branch is really an empty set, revert to prior type + if (type === getUnionType(emptyArray)) { + type = originalType; + } } } @@ -6369,22 +6350,24 @@ 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 the type to be narrowed is any and we're checking a primitive with assumeTrue=true, 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) { - return getUnionType(filter(union.types, t => !!(t.flags & flags))); + let flags: TypeFlags; + if (typeInfo) { + flags = typeInfo.flags; } else { - return getUnionType(filter(union.types, t => !(t.flags & flags))); + assumeTrue = !assumeTrue; + flags = TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol | TypeFlags.Boolean; } + // At this point we can bail if it's not a union + if (!(type.flags & TypeFlags.Union)) { + // If the active non-union type would be removed from a union by this type guard, return an empty union + return (assumeTrue === !!(type.flags & flags)) ? type : getUnionType(emptyArray); + } + return getUnionType(filter((type as UnionType).types, t => assumeTrue === !!(t.flags & flags)), /*noSubtypeReduction*/ true); } function narrowTypeByAnd(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { @@ -12554,7 +12537,13 @@ namespace ts { // After we remove all types that are StringLike, we will know if there was a string constituent // based on whether the remaining type is the same as the initial type. - let arrayType = removeTypesFromUnionType(arrayOrStringType, TypeFlags.StringLike, /*isTypeOfKind*/ true, /*allowEmptyUnionResult*/ true); + let arrayType = arrayOrStringType; + if (arrayOrStringType.flags & TypeFlags.Union) { + arrayType = getUnionType(filter((arrayOrStringType as UnionType).types, t => !(t.flags & TypeFlags.StringLike))); + } + else if (arrayOrStringType.flags & TypeFlags.StringLike) { + arrayType = getUnionType(emptyArray); + } let hasStringConstituent = arrayOrStringType !== arrayType; let reportedError = false; diff --git a/tests/baselines/reference/typeGuardEnums.js b/tests/baselines/reference/typeGuardEnums.js new file mode 100644 index 00000000000..1eb4b7552c4 --- /dev/null +++ b/tests/baselines/reference/typeGuardEnums.js @@ -0,0 +1,41 @@ +//// [typeGuardEnums.ts] +enum E {} +enum V {} + +let x: number|string|E|V; + +if (typeof x === "number") { + x; // number|E|V +} +else { + x; // string +} + +if (typeof x !== "number") { + x; // string +} +else { + x; // number|E|V +} + + +//// [typeGuardEnums.js] +var E; +(function (E) { +})(E || (E = {})); +var V; +(function (V) { +})(V || (V = {})); +var x; +if (typeof x === "number") { + x; // number|E|V +} +else { + x; // string +} +if (typeof x !== "number") { + x; // string +} +else { + x; // number|E|V +} diff --git a/tests/baselines/reference/typeGuardEnums.symbols b/tests/baselines/reference/typeGuardEnums.symbols new file mode 100644 index 00000000000..8f1a396c481 --- /dev/null +++ b/tests/baselines/reference/typeGuardEnums.symbols @@ -0,0 +1,34 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardEnums.ts === +enum E {} +>E : Symbol(E, Decl(typeGuardEnums.ts, 0, 0)) + +enum V {} +>V : Symbol(V, Decl(typeGuardEnums.ts, 0, 9)) + +let x: number|string|E|V; +>x : Symbol(x, Decl(typeGuardEnums.ts, 3, 3)) +>E : Symbol(E, Decl(typeGuardEnums.ts, 0, 0)) +>V : Symbol(V, Decl(typeGuardEnums.ts, 0, 9)) + +if (typeof x === "number") { +>x : Symbol(x, Decl(typeGuardEnums.ts, 3, 3)) + + x; // number|E|V +>x : Symbol(x, Decl(typeGuardEnums.ts, 3, 3)) +} +else { + x; // string +>x : Symbol(x, Decl(typeGuardEnums.ts, 3, 3)) +} + +if (typeof x !== "number") { +>x : Symbol(x, Decl(typeGuardEnums.ts, 3, 3)) + + x; // string +>x : Symbol(x, Decl(typeGuardEnums.ts, 3, 3)) +} +else { + x; // number|E|V +>x : Symbol(x, Decl(typeGuardEnums.ts, 3, 3)) +} + diff --git a/tests/baselines/reference/typeGuardEnums.types b/tests/baselines/reference/typeGuardEnums.types new file mode 100644 index 00000000000..1d39a81d78a --- /dev/null +++ b/tests/baselines/reference/typeGuardEnums.types @@ -0,0 +1,40 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardEnums.ts === +enum E {} +>E : E + +enum V {} +>V : V + +let x: number|string|E|V; +>x : number | string | E | V +>E : E +>V : V + +if (typeof x === "number") { +>typeof x === "number" : boolean +>typeof x : string +>x : number | string | E | V +>"number" : string + + x; // number|E|V +>x : number | E | V +} +else { + x; // string +>x : string +} + +if (typeof x !== "number") { +>typeof x !== "number" : boolean +>typeof x : string +>x : number | string | E | V +>"number" : string + + x; // string +>x : string +} +else { + x; // number|E|V +>x : number | E | V +} + diff --git a/tests/baselines/reference/typeGuardNesting.js b/tests/baselines/reference/typeGuardNesting.js index 4c9a64c2851..364653d0273 100644 --- a/tests/baselines/reference/typeGuardNesting.js +++ b/tests/baselines/reference/typeGuardNesting.js @@ -1,11 +1,31 @@ //// [typeGuardNesting.ts] let strOrBool: string|boolean; if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'string') { - var label: string = (typeof strOrBool === 'string') ? strOrBool : "other string"; -} + let label: string = (typeof strOrBool === 'string') ? strOrBool : "string"; + let bool: boolean = (typeof strOrBool === 'boolean') ? strOrBool : false; + let label2: string = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; + let bool2: boolean = (typeof strOrBool !== 'string') ? strOrBool : false; +} + +if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boolean') { + let label: string = (typeof strOrBool === 'string') ? strOrBool : "string"; + let bool: boolean = (typeof strOrBool === 'boolean') ? strOrBool : false; + let label2: string = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; + let bool2: boolean = (typeof strOrBool !== 'string') ? strOrBool : false; +} + //// [typeGuardNesting.js] var strOrBool; if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'string') { - var label = (typeof strOrBool === 'string') ? strOrBool : "other string"; + var label = (typeof strOrBool === 'string') ? strOrBool : "string"; + var bool = (typeof strOrBool === 'boolean') ? strOrBool : false; + var label2 = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; + var bool2 = (typeof strOrBool !== 'string') ? strOrBool : false; +} +if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boolean') { + var label = (typeof strOrBool === 'string') ? strOrBool : "string"; + var bool = (typeof strOrBool === 'boolean') ? strOrBool : false; + var label2 = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; + var bool2 = (typeof strOrBool !== 'string') ? strOrBool : false; } diff --git a/tests/baselines/reference/typeGuardNesting.symbols b/tests/baselines/reference/typeGuardNesting.symbols index f0cfb1f1eed..427db81f3ed 100644 --- a/tests/baselines/reference/typeGuardNesting.symbols +++ b/tests/baselines/reference/typeGuardNesting.symbols @@ -7,8 +7,50 @@ if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'stri >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"; + let label: string = (typeof strOrBool === 'string') ? strOrBool : "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)) + + let bool: boolean = (typeof strOrBool === 'boolean') ? strOrBool : false; +>bool : Symbol(bool, Decl(typeGuardNesting.ts, 3, 4)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) + + let label2: string = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; +>label2 : Symbol(label2, Decl(typeGuardNesting.ts, 4, 4)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) + + let bool2: boolean = (typeof strOrBool !== 'string') ? strOrBool : false; +>bool2 : Symbol(bool2, Decl(typeGuardNesting.ts, 5, 4)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) >strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) } + +if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boolean') { +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) + + let label: string = (typeof strOrBool === 'string') ? strOrBool : "string"; +>label : Symbol(label, Decl(typeGuardNesting.ts, 9, 4)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) + + let bool: boolean = (typeof strOrBool === 'boolean') ? strOrBool : false; +>bool : Symbol(bool, Decl(typeGuardNesting.ts, 10, 4)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) + + let label2: string = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; +>label2 : Symbol(label2, Decl(typeGuardNesting.ts, 11, 4)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) +>strOrBool : Symbol(strOrBool, Decl(typeGuardNesting.ts, 0, 3)) + + let bool2: boolean = (typeof strOrBool !== 'string') ? strOrBool : false; +>bool2 : Symbol(bool2, Decl(typeGuardNesting.ts, 12, 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 index 9f79974635d..255e96da89e 100644 --- a/tests/baselines/reference/typeGuardNesting.types +++ b/tests/baselines/reference/typeGuardNesting.types @@ -17,14 +17,108 @@ if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'stri >strOrBool : string | boolean >'string' : string - var label: string = (typeof strOrBool === 'string') ? strOrBool : "other string"; + let label: string = (typeof strOrBool === 'string') ? strOrBool : "string"; >label : string ->(typeof strOrBool === 'string') ? strOrBool : "other string" : string +>(typeof strOrBool === 'string') ? strOrBool : "string" : string >(typeof strOrBool === 'string') : boolean >typeof strOrBool === 'string' : boolean >typeof strOrBool : string >strOrBool : boolean | string >'string' : string >strOrBool : string ->"other string" : string +>"string" : string + + let bool: boolean = (typeof strOrBool === 'boolean') ? strOrBool : false; +>bool : boolean +>(typeof strOrBool === 'boolean') ? strOrBool : false : boolean +>(typeof strOrBool === 'boolean') : boolean +>typeof strOrBool === 'boolean' : boolean +>typeof strOrBool : string +>strOrBool : boolean | string +>'boolean' : string +>strOrBool : boolean +>false : boolean + + let label2: string = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; +>label2 : string +>(typeof strOrBool !== 'boolean') ? strOrBool : "string" : string +>(typeof strOrBool !== 'boolean') : boolean +>typeof strOrBool !== 'boolean' : boolean +>typeof strOrBool : string +>strOrBool : boolean | string +>'boolean' : string +>strOrBool : string +>"string" : string + + let bool2: boolean = (typeof strOrBool !== 'string') ? strOrBool : false; +>bool2 : boolean +>(typeof strOrBool !== 'string') ? strOrBool : false : boolean +>(typeof strOrBool !== 'string') : boolean +>typeof strOrBool !== 'string' : boolean +>typeof strOrBool : string +>strOrBool : boolean | string +>'string' : string +>strOrBool : boolean +>false : boolean } + +if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boolean') { +>(typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boolean' : boolean +>(typeof strOrBool !== 'string' && !strOrBool) : boolean +>typeof strOrBool !== 'string' && !strOrBool : boolean +>typeof strOrBool !== 'string' : boolean +>typeof strOrBool : string +>strOrBool : string | boolean +>'string' : string +>!strOrBool : boolean +>strOrBool : boolean +>typeof strOrBool !== 'boolean' : boolean +>typeof strOrBool : string +>strOrBool : string | boolean +>'boolean' : string + + let label: string = (typeof strOrBool === 'string') ? strOrBool : "string"; +>label : string +>(typeof strOrBool === 'string') ? strOrBool : "string" : string +>(typeof strOrBool === 'string') : boolean +>typeof strOrBool === 'string' : boolean +>typeof strOrBool : string +>strOrBool : boolean | string +>'string' : string +>strOrBool : string +>"string" : string + + let bool: boolean = (typeof strOrBool === 'boolean') ? strOrBool : false; +>bool : boolean +>(typeof strOrBool === 'boolean') ? strOrBool : false : boolean +>(typeof strOrBool === 'boolean') : boolean +>typeof strOrBool === 'boolean' : boolean +>typeof strOrBool : string +>strOrBool : boolean | string +>'boolean' : string +>strOrBool : boolean +>false : boolean + + let label2: string = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; +>label2 : string +>(typeof strOrBool !== 'boolean') ? strOrBool : "string" : string +>(typeof strOrBool !== 'boolean') : boolean +>typeof strOrBool !== 'boolean' : boolean +>typeof strOrBool : string +>strOrBool : boolean | string +>'boolean' : string +>strOrBool : string +>"string" : string + + let bool2: boolean = (typeof strOrBool !== 'string') ? strOrBool : false; +>bool2 : boolean +>(typeof strOrBool !== 'string') ? strOrBool : false : boolean +>(typeof strOrBool !== 'string') : boolean +>typeof strOrBool !== 'string' : boolean +>typeof strOrBool : string +>strOrBool : boolean | string +>'string' : string +>strOrBool : boolean +>false : boolean +} + diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.js b/tests/baselines/reference/typeGuardOfFormTypeOfOther.js index 3bb1a2091f6..d1cb4994196 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfOther.js +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.js @@ -23,19 +23,19 @@ if (typeof strOrC === "Object") { c = strOrC; // C } else { - var r2: string | C = strOrC; // string | C + var r2: string = strOrC; // string } if (typeof numOrC === "Object") { c = numOrC; // C } else { - var r3: number | C = numOrC; // number | C + var r3: number = numOrC; // number } if (typeof boolOrC === "Object") { c = boolOrC; // C } else { - var r4: boolean | C = boolOrC; // boolean | C + var r4: boolean = boolOrC; // boolean } // Narrowing occurs only if target type is a subtype of variable type @@ -50,19 +50,19 @@ else { // - when true, narrows the type of x by typeof x === s when false, or // - when false, narrows the type of x by typeof x === s when true. if (typeof strOrC !== "Object") { - var r2: string | C = strOrC; // string | C + var r2: string = strOrC; // string } else { c = strOrC; // C } if (typeof numOrC !== "Object") { - var r3: number | C = numOrC; // number | C + var r3: number = numOrC; // number } else { c = numOrC; // C } if (typeof boolOrC !== "Object") { - var r4: boolean | C = boolOrC; // boolean | C + var r4: boolean = boolOrC; // boolean } else { c = boolOrC; // C @@ -104,19 +104,19 @@ if (typeof strOrC === "Object") { c = strOrC; // C } else { - var r2 = strOrC; // string | C + var r2 = strOrC; // string } if (typeof numOrC === "Object") { c = numOrC; // C } else { - var r3 = numOrC; // number | C + var r3 = numOrC; // number } if (typeof boolOrC === "Object") { c = boolOrC; // C } else { - var r4 = boolOrC; // boolean | C + var r4 = boolOrC; // boolean } // Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool === "Object") { @@ -129,19 +129,19 @@ else { // - when true, narrows the type of x by typeof x === s when false, or // - when false, narrows the type of x by typeof x === s when true. if (typeof strOrC !== "Object") { - var r2 = strOrC; // string | C + var r2 = strOrC; // string } else { c = strOrC; // C } if (typeof numOrC !== "Object") { - var r3 = numOrC; // number | C + var r3 = numOrC; // number } else { c = numOrC; // C } if (typeof boolOrC !== "Object") { - var r4 = boolOrC; // boolean | C + var r4 = boolOrC; // boolean } else { c = boolOrC; // C diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.symbols b/tests/baselines/reference/typeGuardOfFormTypeOfOther.symbols index e3ecffdc2e9..eb120d468dc 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfOther.symbols +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.symbols @@ -56,9 +56,8 @@ if (typeof strOrC === "Object") { >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfOther.ts, 9, 3)) } else { - var r2: string | C = strOrC; // string | C + var r2: string = strOrC; // string >r2 : Symbol(r2, Decl(typeGuardOfFormTypeOfOther.ts, 24, 7), Decl(typeGuardOfFormTypeOfOther.ts, 51, 7)) ->C : Symbol(C, Decl(typeGuardOfFormTypeOfOther.ts, 0, 0)) >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfOther.ts, 9, 3)) } if (typeof numOrC === "Object") { @@ -69,9 +68,8 @@ if (typeof numOrC === "Object") { >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfOther.ts, 10, 3)) } else { - var r3: number | C = numOrC; // number | C + var r3: number = numOrC; // number >r3 : Symbol(r3, Decl(typeGuardOfFormTypeOfOther.ts, 30, 7), Decl(typeGuardOfFormTypeOfOther.ts, 57, 7)) ->C : Symbol(C, Decl(typeGuardOfFormTypeOfOther.ts, 0, 0)) >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfOther.ts, 10, 3)) } if (typeof boolOrC === "Object") { @@ -82,9 +80,8 @@ if (typeof boolOrC === "Object") { >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfOther.ts, 11, 3)) } else { - var r4: boolean | C = boolOrC; // boolean | C + var r4: boolean = boolOrC; // boolean >r4 : Symbol(r4, Decl(typeGuardOfFormTypeOfOther.ts, 36, 7), Decl(typeGuardOfFormTypeOfOther.ts, 63, 7)) ->C : Symbol(C, Decl(typeGuardOfFormTypeOfOther.ts, 0, 0)) >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfOther.ts, 11, 3)) } @@ -108,9 +105,8 @@ else { if (typeof strOrC !== "Object") { >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfOther.ts, 9, 3)) - var r2: string | C = strOrC; // string | C + var r2: string = strOrC; // string >r2 : Symbol(r2, Decl(typeGuardOfFormTypeOfOther.ts, 24, 7), Decl(typeGuardOfFormTypeOfOther.ts, 51, 7)) ->C : Symbol(C, Decl(typeGuardOfFormTypeOfOther.ts, 0, 0)) >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfOther.ts, 9, 3)) } else { @@ -121,9 +117,8 @@ else { if (typeof numOrC !== "Object") { >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfOther.ts, 10, 3)) - var r3: number | C = numOrC; // number | C + var r3: number = numOrC; // number >r3 : Symbol(r3, Decl(typeGuardOfFormTypeOfOther.ts, 30, 7), Decl(typeGuardOfFormTypeOfOther.ts, 57, 7)) ->C : Symbol(C, Decl(typeGuardOfFormTypeOfOther.ts, 0, 0)) >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfOther.ts, 10, 3)) } else { @@ -134,9 +129,8 @@ else { if (typeof boolOrC !== "Object") { >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfOther.ts, 11, 3)) - var r4: boolean | C = boolOrC; // boolean | C + var r4: boolean = boolOrC; // boolean >r4 : Symbol(r4, Decl(typeGuardOfFormTypeOfOther.ts, 36, 7), Decl(typeGuardOfFormTypeOfOther.ts, 63, 7)) ->C : Symbol(C, Decl(typeGuardOfFormTypeOfOther.ts, 0, 0)) >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfOther.ts, 11, 3)) } else { diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.types b/tests/baselines/reference/typeGuardOfFormTypeOfOther.types index 8150c3ec25d..5cec3567194 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfOther.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.types @@ -60,9 +60,8 @@ if (typeof strOrC === "Object") { >strOrC : C } else { - var r2: string | C = strOrC; // string | C ->r2 : string | C ->C : C + var r2: string = strOrC; // string +>r2 : string >strOrC : string } if (typeof numOrC === "Object") { @@ -77,9 +76,8 @@ if (typeof numOrC === "Object") { >numOrC : C } else { - var r3: number | C = numOrC; // number | C ->r3 : number | C ->C : C + var r3: number = numOrC; // number +>r3 : number >numOrC : number } if (typeof boolOrC === "Object") { @@ -94,9 +92,8 @@ if (typeof boolOrC === "Object") { >boolOrC : C } else { - var r4: boolean | C = boolOrC; // boolean | C ->r4 : boolean | C ->C : C + var r4: boolean = boolOrC; // boolean +>r4 : boolean >boolOrC : boolean } @@ -126,9 +123,8 @@ if (typeof strOrC !== "Object") { >strOrC : string | C >"Object" : string - var r2: string | C = strOrC; // string | C ->r2 : string | C ->C : C + var r2: string = strOrC; // string +>r2 : string >strOrC : string } else { @@ -143,9 +139,8 @@ if (typeof numOrC !== "Object") { >numOrC : number | C >"Object" : string - var r3: number | C = numOrC; // number | C ->r3 : number | C ->C : C + var r3: number = numOrC; // number +>r3 : number >numOrC : number } else { @@ -160,9 +155,8 @@ if (typeof boolOrC !== "Object") { >boolOrC : boolean | C >"Object" : string - var r4: boolean | C = boolOrC; // boolean | C ->r4 : boolean | C ->C : C + var r4: boolean = boolOrC; // boolean +>r4 : boolean >boolOrC : boolean } else { diff --git a/tests/baselines/reference/typeGuardTautologicalConsistiency.js b/tests/baselines/reference/typeGuardTautologicalConsistiency.js new file mode 100644 index 00000000000..1b7fa2021c5 --- /dev/null +++ b/tests/baselines/reference/typeGuardTautologicalConsistiency.js @@ -0,0 +1,24 @@ +//// [typeGuardTautologicalConsistiency.ts] +let stringOrNumber: string | number; + +if (typeof stringOrNumber === "number") { + if (typeof stringOrNumber !== "number") { + stringOrNumber; + } +} + +if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { + stringOrNumber; +} + + +//// [typeGuardTautologicalConsistiency.js] +var stringOrNumber; +if (typeof stringOrNumber === "number") { + if (typeof stringOrNumber !== "number") { + stringOrNumber; + } +} +if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { + stringOrNumber; +} diff --git a/tests/baselines/reference/typeGuardTautologicalConsistiency.symbols b/tests/baselines/reference/typeGuardTautologicalConsistiency.symbols new file mode 100644 index 00000000000..9ee82a5413f --- /dev/null +++ b/tests/baselines/reference/typeGuardTautologicalConsistiency.symbols @@ -0,0 +1,23 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardTautologicalConsistiency.ts === +let stringOrNumber: string | number; +>stringOrNumber : Symbol(stringOrNumber, Decl(typeGuardTautologicalConsistiency.ts, 0, 3)) + +if (typeof stringOrNumber === "number") { +>stringOrNumber : Symbol(stringOrNumber, Decl(typeGuardTautologicalConsistiency.ts, 0, 3)) + + if (typeof stringOrNumber !== "number") { +>stringOrNumber : Symbol(stringOrNumber, Decl(typeGuardTautologicalConsistiency.ts, 0, 3)) + + stringOrNumber; +>stringOrNumber : Symbol(stringOrNumber, Decl(typeGuardTautologicalConsistiency.ts, 0, 3)) + } +} + +if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { +>stringOrNumber : Symbol(stringOrNumber, Decl(typeGuardTautologicalConsistiency.ts, 0, 3)) +>stringOrNumber : Symbol(stringOrNumber, Decl(typeGuardTautologicalConsistiency.ts, 0, 3)) + + stringOrNumber; +>stringOrNumber : Symbol(stringOrNumber, Decl(typeGuardTautologicalConsistiency.ts, 0, 3)) +} + diff --git a/tests/baselines/reference/typeGuardTautologicalConsistiency.types b/tests/baselines/reference/typeGuardTautologicalConsistiency.types new file mode 100644 index 00000000000..9acb1464761 --- /dev/null +++ b/tests/baselines/reference/typeGuardTautologicalConsistiency.types @@ -0,0 +1,36 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardTautologicalConsistiency.ts === +let stringOrNumber: string | number; +>stringOrNumber : string | number + +if (typeof stringOrNumber === "number") { +>typeof stringOrNumber === "number" : boolean +>typeof stringOrNumber : string +>stringOrNumber : string | number +>"number" : string + + if (typeof stringOrNumber !== "number") { +>typeof stringOrNumber !== "number" : boolean +>typeof stringOrNumber : string +>stringOrNumber : number +>"number" : string + + stringOrNumber; +>stringOrNumber : string + } +} + +if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { +>typeof stringOrNumber === "number" && typeof stringOrNumber !== "number" : boolean +>typeof stringOrNumber === "number" : boolean +>typeof stringOrNumber : string +>stringOrNumber : string | number +>"number" : string +>typeof stringOrNumber !== "number" : boolean +>typeof stringOrNumber : string +>stringOrNumber : number +>"number" : string + + stringOrNumber; +>stringOrNumber : number +} + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardEnums.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardEnums.ts new file mode 100644 index 00000000000..54e9a1bd8fe --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardEnums.ts @@ -0,0 +1,18 @@ +enum E {} +enum V {} + +let x: number|string|E|V; + +if (typeof x === "number") { + x; // number|E|V +} +else { + x; // string +} + +if (typeof x !== "number") { + x; // string +} +else { + x; // number|E|V +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNesting.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNesting.ts index b06b5880800..6423af18a5a 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardNesting.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNesting.ts @@ -1,4 +1,14 @@ let strOrBool: string|boolean; if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'string') { - var label: string = (typeof strOrBool === 'string') ? strOrBool : "other string"; -} \ No newline at end of file + let label: string = (typeof strOrBool === 'string') ? strOrBool : "string"; + let bool: boolean = (typeof strOrBool === 'boolean') ? strOrBool : false; + let label2: string = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; + let bool2: boolean = (typeof strOrBool !== 'string') ? strOrBool : false; +} + +if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boolean') { + let label: string = (typeof strOrBool === 'string') ? strOrBool : "string"; + let bool: boolean = (typeof strOrBool === 'boolean') ? strOrBool : false; + let label2: string = (typeof strOrBool !== 'boolean') ? strOrBool : "string"; + let bool2: boolean = (typeof strOrBool !== 'string') ? strOrBool : false; +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts index 9d7d555fc18..ac3403d8151 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts @@ -22,19 +22,19 @@ if (typeof strOrC === "Object") { c = strOrC; // C } else { - var r2: string | C = strOrC; // string | C + var r2: string = strOrC; // string } if (typeof numOrC === "Object") { c = numOrC; // C } else { - var r3: number | C = numOrC; // number | C + var r3: number = numOrC; // number } if (typeof boolOrC === "Object") { c = boolOrC; // C } else { - var r4: boolean | C = boolOrC; // boolean | C + var r4: boolean = boolOrC; // boolean } // Narrowing occurs only if target type is a subtype of variable type @@ -49,19 +49,19 @@ else { // - when true, narrows the type of x by typeof x === s when false, or // - when false, narrows the type of x by typeof x === s when true. if (typeof strOrC !== "Object") { - var r2: string | C = strOrC; // string | C + var r2: string = strOrC; // string } else { c = strOrC; // C } if (typeof numOrC !== "Object") { - var r3: number | C = numOrC; // number | C + var r3: number = numOrC; // number } else { c = numOrC; // C } if (typeof boolOrC !== "Object") { - var r4: boolean | C = boolOrC; // boolean | C + var r4: boolean = boolOrC; // boolean } else { c = boolOrC; // C diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardTautologicalConsistiency.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardTautologicalConsistiency.ts new file mode 100644 index 00000000000..9a44603d2d4 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardTautologicalConsistiency.ts @@ -0,0 +1,11 @@ +let stringOrNumber: string | number; + +if (typeof stringOrNumber === "number") { + if (typeof stringOrNumber !== "number") { + stringOrNumber; + } +} + +if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { + stringOrNumber; +}