diff --git a/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.js b/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.js new file mode 100644 index 00000000000..42bab3cdbe7 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.js @@ -0,0 +1,97 @@ +//// [typeGuardOfFormExpr1AndExpr2.ts] +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrNumOrBool: string | number | boolean; +var numOrBool: number | boolean; +class C { private p; } +var c: C; +var cOrBool: C| boolean; +var strOrNumOrBoolOrC: string | number | boolean | C; + +// A type guard of the form expr1 && expr2 +// - when true, narrows the type of x by expr1 when true and then by expr2 when true, or +// - when false, narrows the type of x to T1 | T2, where T1 is the type of x narrowed by expr1 when +// false, and T2 is the type of x narrowed by expr1 when true and then by expr2 when false. + +// (typeguard1 && typeguard2) +if (typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number") { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// (typeguard1 && typeguard2 && typeguard3) +if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBoolOrC !== "boolean") { + c = strOrNumOrBoolOrC; // C +} +else { + strOrNumOrBool = strOrNumOrBoolOrC; // string | number | boolean +} +// (typeguard1 && typeguard2 && typeguard11(onAnotherType)) +if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBool === "boolean") { + cOrBool = strOrNumOrBoolOrC; // C | boolean + bool = strOrNumOrBool; // boolean +} +else { + var r1: string | number | boolean | C = strOrNumOrBoolOrC; // string | number | boolean | C + var r2: string | number | boolean = strOrNumOrBool; +} +// (typeguard1) && simpleExpr +if (typeof strOrNumOrBool !== "string" && numOrBool !== strOrNumOrBool) { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + var r3: string | number | boolean = strOrNumOrBool; // string | number | boolean +} + +//// [typeGuardOfFormExpr1AndExpr2.js] +var str; +var bool; +var num; +var strOrNum; +var strOrNumOrBool; +var numOrBool; +var C = (function () { + function C() { + } + return C; +})(); +var c; +var cOrBool; +var strOrNumOrBoolOrC; +// A type guard of the form expr1 && expr2 +// - when true, narrows the type of x by expr1 when true and then by expr2 when true, or +// - when false, narrows the type of x to T1 | T2, where T1 is the type of x narrowed by expr1 when +// false, and T2 is the type of x narrowed by expr1 when true and then by expr2 when false. +// (typeguard1 && typeguard2) +if (typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number") { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// (typeguard1 && typeguard2 && typeguard3) +if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBoolOrC !== "boolean") { + c = strOrNumOrBoolOrC; // C +} +else { + strOrNumOrBool = strOrNumOrBoolOrC; // string | number | boolean +} +// (typeguard1 && typeguard2 && typeguard11(onAnotherType)) +if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBool === "boolean") { + cOrBool = strOrNumOrBoolOrC; // C | boolean + bool = strOrNumOrBool; // boolean +} +else { + var r1 = strOrNumOrBoolOrC; // string | number | boolean | C + var r2 = strOrNumOrBool; +} +// (typeguard1) && simpleExpr +if (typeof strOrNumOrBool !== "string" && numOrBool !== strOrNumOrBool) { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + var r3 = strOrNumOrBool; // string | number | boolean +} diff --git a/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.types b/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.types new file mode 100644 index 00000000000..f599f1f7ddf --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.types @@ -0,0 +1,140 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormExpr1AndExpr2.ts === +var str: string; +>str : string + +var bool: boolean; +>bool : boolean + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrNumOrBool: string | number | boolean; +>strOrNumOrBool : string | number | boolean + +var numOrBool: number | boolean; +>numOrBool : number | boolean + +class C { private p; } +>C : C +>p : any + +var c: C; +>c : C +>C : C + +var cOrBool: C| boolean; +>cOrBool : boolean | C +>C : C + +var strOrNumOrBoolOrC: string | number | boolean | C; +>strOrNumOrBoolOrC : string | number | boolean | C +>C : C + +// A type guard of the form expr1 && expr2 +// - when true, narrows the type of x by expr1 when true and then by expr2 when true, or +// - when false, narrows the type of x to T1 | T2, where T1 is the type of x narrowed by expr1 when +// false, and T2 is the type of x narrowed by expr1 when true and then by expr2 when false. + +// (typeguard1 && typeguard2) +if (typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number") { +>typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number" : boolean +>typeof strOrNumOrBool !== "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>typeof strOrNumOrBool !== "number" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : number | boolean + + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +>strOrNum = strOrNumOrBool : string | number +>strOrNum : string | number +>strOrNumOrBool : string | number +} +// (typeguard1 && typeguard2 && typeguard3) +if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBoolOrC !== "boolean") { +>typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBoolOrC !== "boolean" : boolean +>typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" : boolean +>typeof strOrNumOrBoolOrC !== "string" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : string | number | boolean | C +>typeof strOrNumOrBoolOrC !== "number" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : number | boolean | C +>typeof strOrNumOrBoolOrC !== "boolean" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : boolean | C + + c = strOrNumOrBoolOrC; // C +>c = strOrNumOrBoolOrC : C +>c : C +>strOrNumOrBoolOrC : C +} +else { + strOrNumOrBool = strOrNumOrBoolOrC; // string | number | boolean +>strOrNumOrBool = strOrNumOrBoolOrC : string | number | boolean +>strOrNumOrBool : string | number | boolean +>strOrNumOrBoolOrC : string | number | boolean +} +// (typeguard1 && typeguard2 && typeguard11(onAnotherType)) +if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBool === "boolean") { +>typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBool === "boolean" : boolean +>typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" : boolean +>typeof strOrNumOrBoolOrC !== "string" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : string | number | boolean | C +>typeof strOrNumOrBoolOrC !== "number" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : number | boolean | C +>typeof strOrNumOrBool === "boolean" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + cOrBool = strOrNumOrBoolOrC; // C | boolean +>cOrBool = strOrNumOrBoolOrC : boolean | C +>cOrBool : boolean | C +>strOrNumOrBoolOrC : boolean | C + + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +else { + var r1: string | number | boolean | C = strOrNumOrBoolOrC; // string | number | boolean | C +>r1 : string | number | boolean | C +>C : C +>strOrNumOrBoolOrC : string | number | boolean | C + + var r2: string | number | boolean = strOrNumOrBool; +>r2 : string | number | boolean +>strOrNumOrBool : string | number | boolean +} +// (typeguard1) && simpleExpr +if (typeof strOrNumOrBool !== "string" && numOrBool !== strOrNumOrBool) { +>typeof strOrNumOrBool !== "string" && numOrBool !== strOrNumOrBool : boolean +>typeof strOrNumOrBool !== "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>numOrBool !== strOrNumOrBool : boolean +>numOrBool : number | boolean +>strOrNumOrBool : number | boolean + + numOrBool = strOrNumOrBool; // number | boolean +>numOrBool = strOrNumOrBool : number | boolean +>numOrBool : number | boolean +>strOrNumOrBool : number | boolean +} +else { + var r3: string | number | boolean = strOrNumOrBool; // string | number | boolean +>r3 : string | number | boolean +>strOrNumOrBool : string | number | boolean +} diff --git a/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.js b/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.js new file mode 100644 index 00000000000..7e38232a738 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.js @@ -0,0 +1,97 @@ +//// [typeGuardOfFormExpr1OrExpr2.ts] +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrNumOrBool: string | number | boolean; +var numOrBool: number | boolean; +class C { private p; } +var c: C; +var cOrBool: C| boolean; +var strOrNumOrBoolOrC: string | number | boolean | C; + +// A type guard of the form expr1 || expr2 +// - when true, narrows the type of x to T1 | T2, where T1 is the type of x narrowed by expr1 when true, +// and T2 is the type of x narrowed by expr1 when false and then by expr2 when true, or +// - when false, narrows the type of x by expr1 when false and then by expr2 when false. + +// (typeguard1 || typeguard2) +if (typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number") { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// (typeguard1 || typeguard2 || typeguard3) +if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBoolOrC === "boolean") { + strOrNumOrBool = strOrNumOrBoolOrC; // string | number | boolean +} +else { + c = strOrNumOrBoolOrC; // C +} +// (typeguard1 || typeguard2 || typeguard11(onAnotherType)) +if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBool !== "boolean") { + var r1: string | number | boolean | C = strOrNumOrBoolOrC; // string | number | boolean | C + var r2: string | number | boolean = strOrNumOrBool; +} +else { + cOrBool = strOrNumOrBoolOrC; // C | boolean + bool = strOrNumOrBool; // boolean +} +// (typeguard1) || simpleExpr +if (typeof strOrNumOrBool === "string" || numOrBool !== strOrNumOrBool) { + var r3: string | number | boolean = strOrNumOrBool; // string | number | boolean +} +else { + numOrBool = strOrNumOrBool; // number | boolean +} + +//// [typeGuardOfFormExpr1OrExpr2.js] +var str; +var bool; +var num; +var strOrNum; +var strOrNumOrBool; +var numOrBool; +var C = (function () { + function C() { + } + return C; +})(); +var c; +var cOrBool; +var strOrNumOrBoolOrC; +// A type guard of the form expr1 || expr2 +// - when true, narrows the type of x to T1 | T2, where T1 is the type of x narrowed by expr1 when true, +// and T2 is the type of x narrowed by expr1 when false and then by expr2 when true, or +// - when false, narrows the type of x by expr1 when false and then by expr2 when false. +// (typeguard1 || typeguard2) +if (typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number") { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// (typeguard1 || typeguard2 || typeguard3) +if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBoolOrC === "boolean") { + strOrNumOrBool = strOrNumOrBoolOrC; // string | number | boolean +} +else { + c = strOrNumOrBoolOrC; // C +} +// (typeguard1 || typeguard2 || typeguard11(onAnotherType)) +if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBool !== "boolean") { + var r1 = strOrNumOrBoolOrC; // string | number | boolean | C + var r2 = strOrNumOrBool; +} +else { + cOrBool = strOrNumOrBoolOrC; // C | boolean + bool = strOrNumOrBool; // boolean +} +// (typeguard1) || simpleExpr +if (typeof strOrNumOrBool === "string" || numOrBool !== strOrNumOrBool) { + var r3 = strOrNumOrBool; // string | number | boolean +} +else { + numOrBool = strOrNumOrBool; // number | boolean +} diff --git a/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.types b/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.types new file mode 100644 index 00000000000..95152a589e5 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.types @@ -0,0 +1,140 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormExpr1OrExpr2.ts === +var str: string; +>str : string + +var bool: boolean; +>bool : boolean + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrNumOrBool: string | number | boolean; +>strOrNumOrBool : string | number | boolean + +var numOrBool: number | boolean; +>numOrBool : number | boolean + +class C { private p; } +>C : C +>p : any + +var c: C; +>c : C +>C : C + +var cOrBool: C| boolean; +>cOrBool : boolean | C +>C : C + +var strOrNumOrBoolOrC: string | number | boolean | C; +>strOrNumOrBoolOrC : string | number | boolean | C +>C : C + +// A type guard of the form expr1 || expr2 +// - when true, narrows the type of x to T1 | T2, where T1 is the type of x narrowed by expr1 when true, +// and T2 is the type of x narrowed by expr1 when false and then by expr2 when true, or +// - when false, narrows the type of x by expr1 when false and then by expr2 when false. + +// (typeguard1 || typeguard2) +if (typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number") { +>typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number" : boolean +>typeof strOrNumOrBool === "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>typeof strOrNumOrBool === "number" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : number | boolean + + strOrNum = strOrNumOrBool; // string | number +>strOrNum = strOrNumOrBool : string | number +>strOrNum : string | number +>strOrNumOrBool : string | number +} +else { + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +// (typeguard1 || typeguard2 || typeguard3) +if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBoolOrC === "boolean") { +>typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBoolOrC === "boolean" : boolean +>typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" : boolean +>typeof strOrNumOrBoolOrC === "string" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : string | number | boolean | C +>typeof strOrNumOrBoolOrC === "number" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : number | boolean | C +>typeof strOrNumOrBoolOrC === "boolean" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : boolean | C + + strOrNumOrBool = strOrNumOrBoolOrC; // string | number | boolean +>strOrNumOrBool = strOrNumOrBoolOrC : string | number | boolean +>strOrNumOrBool : string | number | boolean +>strOrNumOrBoolOrC : string | number | boolean +} +else { + c = strOrNumOrBoolOrC; // C +>c = strOrNumOrBoolOrC : C +>c : C +>strOrNumOrBoolOrC : C +} +// (typeguard1 || typeguard2 || typeguard11(onAnotherType)) +if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBool !== "boolean") { +>typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBool !== "boolean" : boolean +>typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" : boolean +>typeof strOrNumOrBoolOrC === "string" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : string | number | boolean | C +>typeof strOrNumOrBoolOrC === "number" : boolean +>typeof strOrNumOrBoolOrC : string +>strOrNumOrBoolOrC : number | boolean | C +>typeof strOrNumOrBool !== "boolean" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + var r1: string | number | boolean | C = strOrNumOrBoolOrC; // string | number | boolean | C +>r1 : string | number | boolean | C +>C : C +>strOrNumOrBoolOrC : string | number | boolean | C + + var r2: string | number | boolean = strOrNumOrBool; +>r2 : string | number | boolean +>strOrNumOrBool : string | number | boolean +} +else { + cOrBool = strOrNumOrBoolOrC; // C | boolean +>cOrBool = strOrNumOrBoolOrC : boolean | C +>cOrBool : boolean | C +>strOrNumOrBoolOrC : boolean | C + + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +// (typeguard1) || simpleExpr +if (typeof strOrNumOrBool === "string" || numOrBool !== strOrNumOrBool) { +>typeof strOrNumOrBool === "string" || numOrBool !== strOrNumOrBool : boolean +>typeof strOrNumOrBool === "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>numOrBool !== strOrNumOrBool : boolean +>numOrBool : number | boolean +>strOrNumOrBool : number | boolean + + var r3: string | number | boolean = strOrNumOrBool; // string | number | boolean +>r3 : string | number | boolean +>strOrNumOrBool : string | number | boolean +} +else { + numOrBool = strOrNumOrBool; // number | boolean +>numOrBool = strOrNumOrBool : number | boolean +>numOrBool : number | boolean +>strOrNumOrBool : number | boolean +} diff --git a/tests/baselines/reference/typeGuardOfFormInstanceOf.js b/tests/baselines/reference/typeGuardOfFormInstanceOf.js new file mode 100644 index 00000000000..093e80cbb74 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormInstanceOf.js @@ -0,0 +1,74 @@ +//// [typeGuardOfFormInstanceOf.ts] +// A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' +// and C has a property named 'prototype' +// - when true, narrows the type of x to the type of the 'prototype' property in C provided +// it is a subtype of the type of x, or +// - when false, has no effect on the type of x. + +class C1 { + p1: string; +} +class C2 { + p2: number; +} +class D1 extends C1 { + p3: number; +} +var str: string; +var num: number; +var strOrNum: string | number; + +var c1Orc2: C1 | C2; +str = c1Orc2 instanceof C1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof C2 && c1Orc2.p2; // C2 +str = c1Orc2 instanceof D1 && c1Orc2.p1; // D1 +num = c1Orc2 instanceof D1 && c1Orc2.p3; // D1 + +var c2Ord1: C2 | D1; +num = c2Ord1 instanceof C2 && c2Ord1.p2; // C2 +num = c2Ord1 instanceof D1 && c2Ord1.p3; // D1 +str = c2Ord1 instanceof D1 && c2Ord1.p1; // D1 +var r2: D1 | C2 = c2Ord1 instanceof C1 && c2Ord1; // C2 | D1 + +//// [typeGuardOfFormInstanceOf.js] +// A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' +// and C has a property named 'prototype' +// - when true, narrows the type of x to the type of the 'prototype' property in C provided +// it is a subtype of the type of x, or +// - when false, has no effect on the type of x. +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var C1 = (function () { + function C1() { + } + return C1; +})(); +var C2 = (function () { + function C2() { + } + return C2; +})(); +var D1 = (function (_super) { + __extends(D1, _super); + function D1() { + _super.apply(this, arguments); + } + return D1; +})(C1); +var str; +var num; +var strOrNum; +var c1Orc2; +str = c1Orc2 instanceof C1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof C2 && c1Orc2.p2; // C2 +str = c1Orc2 instanceof D1 && c1Orc2.p1; // D1 +num = c1Orc2 instanceof D1 && c1Orc2.p3; // D1 +var c2Ord1; +num = c2Ord1 instanceof C2 && c2Ord1.p2; // C2 +num = c2Ord1 instanceof D1 && c2Ord1.p3; // D1 +str = c2Ord1 instanceof D1 && c2Ord1.p1; // D1 +var r2 = c2Ord1 instanceof C1 && c2Ord1; // C2 | D1 diff --git a/tests/baselines/reference/typeGuardOfFormInstanceOf.types b/tests/baselines/reference/typeGuardOfFormInstanceOf.types new file mode 100644 index 00000000000..fb44482c86e --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormInstanceOf.types @@ -0,0 +1,132 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormInstanceOf.ts === +// A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' +// and C has a property named 'prototype' +// - when true, narrows the type of x to the type of the 'prototype' property in C provided +// it is a subtype of the type of x, or +// - when false, has no effect on the type of x. + +class C1 { +>C1 : C1 + + p1: string; +>p1 : string +} +class C2 { +>C2 : C2 + + p2: number; +>p2 : number +} +class D1 extends C1 { +>D1 : D1 +>C1 : C1 + + p3: number; +>p3 : number +} +var str: string; +>str : string + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var c1Orc2: C1 | C2; +>c1Orc2 : C1 | C2 +>C1 : C1 +>C2 : C2 + +str = c1Orc2 instanceof C1 && c1Orc2.p1; // C1 +>str = c1Orc2 instanceof C1 && c1Orc2.p1 : string +>str : string +>c1Orc2 instanceof C1 && c1Orc2.p1 : string +>c1Orc2 instanceof C1 : boolean +>c1Orc2 : C1 | C2 +>C1 : typeof C1 +>c1Orc2.p1 : string +>c1Orc2 : C1 +>p1 : string + +num = c1Orc2 instanceof C2 && c1Orc2.p2; // C2 +>num = c1Orc2 instanceof C2 && c1Orc2.p2 : number +>num : number +>c1Orc2 instanceof C2 && c1Orc2.p2 : number +>c1Orc2 instanceof C2 : boolean +>c1Orc2 : C1 | C2 +>C2 : typeof C2 +>c1Orc2.p2 : number +>c1Orc2 : C2 +>p2 : number + +str = c1Orc2 instanceof D1 && c1Orc2.p1; // D1 +>str = c1Orc2 instanceof D1 && c1Orc2.p1 : string +>str : string +>c1Orc2 instanceof D1 && c1Orc2.p1 : string +>c1Orc2 instanceof D1 : boolean +>c1Orc2 : C1 | C2 +>D1 : typeof D1 +>c1Orc2.p1 : string +>c1Orc2 : D1 +>p1 : string + +num = c1Orc2 instanceof D1 && c1Orc2.p3; // D1 +>num = c1Orc2 instanceof D1 && c1Orc2.p3 : number +>num : number +>c1Orc2 instanceof D1 && c1Orc2.p3 : number +>c1Orc2 instanceof D1 : boolean +>c1Orc2 : C1 | C2 +>D1 : typeof D1 +>c1Orc2.p3 : number +>c1Orc2 : D1 +>p3 : number + +var c2Ord1: C2 | D1; +>c2Ord1 : C2 | D1 +>C2 : C2 +>D1 : D1 + +num = c2Ord1 instanceof C2 && c2Ord1.p2; // C2 +>num = c2Ord1 instanceof C2 && c2Ord1.p2 : number +>num : number +>c2Ord1 instanceof C2 && c2Ord1.p2 : number +>c2Ord1 instanceof C2 : boolean +>c2Ord1 : C2 | D1 +>C2 : typeof C2 +>c2Ord1.p2 : number +>c2Ord1 : C2 +>p2 : number + +num = c2Ord1 instanceof D1 && c2Ord1.p3; // D1 +>num = c2Ord1 instanceof D1 && c2Ord1.p3 : number +>num : number +>c2Ord1 instanceof D1 && c2Ord1.p3 : number +>c2Ord1 instanceof D1 : boolean +>c2Ord1 : C2 | D1 +>D1 : typeof D1 +>c2Ord1.p3 : number +>c2Ord1 : D1 +>p3 : number + +str = c2Ord1 instanceof D1 && c2Ord1.p1; // D1 +>str = c2Ord1 instanceof D1 && c2Ord1.p1 : string +>str : string +>c2Ord1 instanceof D1 && c2Ord1.p1 : string +>c2Ord1 instanceof D1 : boolean +>c2Ord1 : C2 | D1 +>D1 : typeof D1 +>c2Ord1.p1 : string +>c2Ord1 : D1 +>p1 : string + +var r2: D1 | C2 = c2Ord1 instanceof C1 && c2Ord1; // C2 | D1 +>r2 : C2 | D1 +>D1 : D1 +>C2 : C2 +>c2Ord1 instanceof C1 && c2Ord1 : C2 | D1 +>c2Ord1 instanceof C1 : boolean +>c2Ord1 : C2 | D1 +>C1 : typeof C1 +>c2Ord1 : C2 | D1 + diff --git a/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.js b/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.js new file mode 100644 index 00000000000..2f40be79ac4 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.js @@ -0,0 +1,62 @@ +//// [typeGuardOfFormInstanceOfOnInterface.ts] +// A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' +// and C has a property named 'prototype' +// - when true, narrows the type of x to the type of the 'prototype' property in C provided +// it is a subtype of the type of x, or +// - when false, has no effect on the type of x. + +interface C1 { + (): C1; + prototype: C1; + p1: string; +} +interface C2 { + (): C2; + prototype: C2; + p2: number; +} +interface D1 extends C1 { + prototype: D1; + p3: number; +} +var str: string; +var num: number; +var strOrNum: string | number; + +var c1: C1; +var c2: C2; +var d1: D1; +var c1Orc2: C1 | C2; +str = c1Orc2 instanceof c1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof c2 && c1Orc2.p2; // C2 +str = c1Orc2 instanceof d1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof d1 && c1Orc2.p3; // D1 + +var c2Ord1: C2 | D1; +num = c2Ord1 instanceof c2 && c2Ord1.p2; // C2 +num = c2Ord1 instanceof d1 && c2Ord1.p3; // D1 +str = c2Ord1 instanceof d1 && c2Ord1.p1; // D1 +var r2: D1 | C2 = c2Ord1 instanceof c1 && c2Ord1; // C2 | D1 + +//// [typeGuardOfFormInstanceOfOnInterface.js] +// A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' +// and C has a property named 'prototype' +// - when true, narrows the type of x to the type of the 'prototype' property in C provided +// it is a subtype of the type of x, or +// - when false, has no effect on the type of x. +var str; +var num; +var strOrNum; +var c1; +var c2; +var d1; +var c1Orc2; +str = c1Orc2 instanceof c1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof c2 && c1Orc2.p2; // C2 +str = c1Orc2 instanceof d1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof d1 && c1Orc2.p3; // D1 +var c2Ord1; +num = c2Ord1 instanceof c2 && c2Ord1.p2; // C2 +num = c2Ord1 instanceof d1 && c2Ord1.p3; // D1 +str = c2Ord1 instanceof d1 && c2Ord1.p1; // D1 +var r2 = c2Ord1 instanceof c1 && c2Ord1; // C2 | D1 diff --git a/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.types b/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.types new file mode 100644 index 00000000000..7bf67da1500 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.types @@ -0,0 +1,162 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormInstanceOfOnInterface.ts === +// A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' +// and C has a property named 'prototype' +// - when true, narrows the type of x to the type of the 'prototype' property in C provided +// it is a subtype of the type of x, or +// - when false, has no effect on the type of x. + +interface C1 { +>C1 : C1 + + (): C1; +>C1 : C1 + + prototype: C1; +>prototype : C1 +>C1 : C1 + + p1: string; +>p1 : string +} +interface C2 { +>C2 : C2 + + (): C2; +>C2 : C2 + + prototype: C2; +>prototype : C2 +>C2 : C2 + + p2: number; +>p2 : number +} +interface D1 extends C1 { +>D1 : D1 +>C1 : C1 + + prototype: D1; +>prototype : D1 +>D1 : D1 + + p3: number; +>p3 : number +} +var str: string; +>str : string + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var c1: C1; +>c1 : C1 +>C1 : C1 + +var c2: C2; +>c2 : C2 +>C2 : C2 + +var d1: D1; +>d1 : D1 +>D1 : D1 + +var c1Orc2: C1 | C2; +>c1Orc2 : C1 | C2 +>C1 : C1 +>C2 : C2 + +str = c1Orc2 instanceof c1 && c1Orc2.p1; // C1 +>str = c1Orc2 instanceof c1 && c1Orc2.p1 : string +>str : string +>c1Orc2 instanceof c1 && c1Orc2.p1 : string +>c1Orc2 instanceof c1 : boolean +>c1Orc2 : C1 | C2 +>c1 : C1 +>c1Orc2.p1 : string +>c1Orc2 : C1 +>p1 : string + +num = c1Orc2 instanceof c2 && c1Orc2.p2; // C2 +>num = c1Orc2 instanceof c2 && c1Orc2.p2 : number +>num : number +>c1Orc2 instanceof c2 && c1Orc2.p2 : number +>c1Orc2 instanceof c2 : boolean +>c1Orc2 : C1 | C2 +>c2 : C2 +>c1Orc2.p2 : number +>c1Orc2 : C2 +>p2 : number + +str = c1Orc2 instanceof d1 && c1Orc2.p1; // C1 +>str = c1Orc2 instanceof d1 && c1Orc2.p1 : string +>str : string +>c1Orc2 instanceof d1 && c1Orc2.p1 : string +>c1Orc2 instanceof d1 : boolean +>c1Orc2 : C1 | C2 +>d1 : D1 +>c1Orc2.p1 : string +>c1Orc2 : D1 +>p1 : string + +num = c1Orc2 instanceof d1 && c1Orc2.p3; // D1 +>num = c1Orc2 instanceof d1 && c1Orc2.p3 : number +>num : number +>c1Orc2 instanceof d1 && c1Orc2.p3 : number +>c1Orc2 instanceof d1 : boolean +>c1Orc2 : C1 | C2 +>d1 : D1 +>c1Orc2.p3 : number +>c1Orc2 : D1 +>p3 : number + +var c2Ord1: C2 | D1; +>c2Ord1 : C2 | D1 +>C2 : C2 +>D1 : D1 + +num = c2Ord1 instanceof c2 && c2Ord1.p2; // C2 +>num = c2Ord1 instanceof c2 && c2Ord1.p2 : number +>num : number +>c2Ord1 instanceof c2 && c2Ord1.p2 : number +>c2Ord1 instanceof c2 : boolean +>c2Ord1 : C2 | D1 +>c2 : C2 +>c2Ord1.p2 : number +>c2Ord1 : C2 +>p2 : number + +num = c2Ord1 instanceof d1 && c2Ord1.p3; // D1 +>num = c2Ord1 instanceof d1 && c2Ord1.p3 : number +>num : number +>c2Ord1 instanceof d1 && c2Ord1.p3 : number +>c2Ord1 instanceof d1 : boolean +>c2Ord1 : C2 | D1 +>d1 : D1 +>c2Ord1.p3 : number +>c2Ord1 : D1 +>p3 : number + +str = c2Ord1 instanceof d1 && c2Ord1.p1; // D1 +>str = c2Ord1 instanceof d1 && c2Ord1.p1 : string +>str : string +>c2Ord1 instanceof d1 && c2Ord1.p1 : string +>c2Ord1 instanceof d1 : boolean +>c2Ord1 : C2 | D1 +>d1 : D1 +>c2Ord1.p1 : string +>c2Ord1 : D1 +>p1 : string + +var r2: D1 | C2 = c2Ord1 instanceof c1 && c2Ord1; // C2 | D1 +>r2 : C2 | D1 +>D1 : D1 +>C2 : C2 +>c2Ord1 instanceof c1 && c2Ord1 : C2 | D1 +>c2Ord1 instanceof c1 : boolean +>c2Ord1 : C2 | D1 +>c1 : C1 +>c2Ord1 : C2 | D1 + diff --git a/tests/baselines/reference/typeGuardOfFormNotExpr.js b/tests/baselines/reference/typeGuardOfFormNotExpr.js new file mode 100644 index 00000000000..62a1b3ff940 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormNotExpr.js @@ -0,0 +1,107 @@ +//// [typeGuardOfFormNotExpr.ts] +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrNumOrBool: string | number | boolean; +var numOrBool: number | boolean; + +// A type guard of the form !expr +// - when true, narrows the type of x by expr when false, or +// - when false, narrows the type of x by expr when true. + +// !typeguard1 +if (!(typeof strOrNum === "string")) { + num === strOrNum; // number +} +else { + str = strOrNum; // string +} +// !(typeguard1 || typeguard2) +if (!(typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number")) { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// !(typeguard1) || !(typeguard2) +if (!(typeof strOrNumOrBool !== "string") || !(typeof strOrNumOrBool !== "number")) { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// !(typeguard1 && typeguard2) +if (!(typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number")) { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// !(typeguard1) && !(typeguard2) +if (!(typeof strOrNumOrBool === "string") && !(typeof strOrNumOrBool === "number")) { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// !(typeguard1) && simpleExpr +if (!(typeof strOrNumOrBool === "string") && numOrBool !== strOrNumOrBool) { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +} + +//// [typeGuardOfFormNotExpr.js] +var str; +var bool; +var num; +var strOrNum; +var strOrNumOrBool; +var numOrBool; +// A type guard of the form !expr +// - when true, narrows the type of x by expr when false, or +// - when false, narrows the type of x by expr when true. +// !typeguard1 +if (!(typeof strOrNum === "string")) { + num === strOrNum; // number +} +else { + str = strOrNum; // string +} +// !(typeguard1 || typeguard2) +if (!(typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number")) { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// !(typeguard1) || !(typeguard2) +if (!(typeof strOrNumOrBool !== "string") || !(typeof strOrNumOrBool !== "number")) { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// !(typeguard1 && typeguard2) +if (!(typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number")) { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// !(typeguard1) && !(typeguard2) +if (!(typeof strOrNumOrBool === "string") && !(typeof strOrNumOrBool === "number")) { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// !(typeguard1) && simpleExpr +if (!(typeof strOrNumOrBool === "string") && numOrBool !== strOrNumOrBool) { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + var r1 = strOrNumOrBool; // string | number | boolean +} diff --git a/tests/baselines/reference/typeGuardOfFormNotExpr.types b/tests/baselines/reference/typeGuardOfFormNotExpr.types new file mode 100644 index 00000000000..22d1d71f412 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormNotExpr.types @@ -0,0 +1,160 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormNotExpr.ts === +var str: string; +>str : string + +var bool: boolean; +>bool : boolean + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrNumOrBool: string | number | boolean; +>strOrNumOrBool : string | number | boolean + +var numOrBool: number | boolean; +>numOrBool : number | boolean + +// A type guard of the form !expr +// - when true, narrows the type of x by expr when false, or +// - when false, narrows the type of x by expr when true. + +// !typeguard1 +if (!(typeof strOrNum === "string")) { +>!(typeof strOrNum === "string") : boolean +>(typeof strOrNum === "string") : boolean +>typeof strOrNum === "string" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + num === strOrNum; // number +>num === strOrNum : boolean +>num : number +>strOrNum : number +} +else { + str = strOrNum; // string +>str = strOrNum : string +>str : string +>strOrNum : string +} +// !(typeguard1 || typeguard2) +if (!(typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number")) { +>!(typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number") : boolean +>(typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number") : boolean +>typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number" : boolean +>typeof strOrNumOrBool === "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>typeof strOrNumOrBool === "number" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : number | boolean + + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +>strOrNum = strOrNumOrBool : string | number +>strOrNum : string | number +>strOrNumOrBool : string | number +} +// !(typeguard1) || !(typeguard2) +if (!(typeof strOrNumOrBool !== "string") || !(typeof strOrNumOrBool !== "number")) { +>!(typeof strOrNumOrBool !== "string") || !(typeof strOrNumOrBool !== "number") : boolean +>!(typeof strOrNumOrBool !== "string") : boolean +>(typeof strOrNumOrBool !== "string") : boolean +>typeof strOrNumOrBool !== "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>!(typeof strOrNumOrBool !== "number") : boolean +>(typeof strOrNumOrBool !== "number") : boolean +>typeof strOrNumOrBool !== "number" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : number | boolean + + strOrNum = strOrNumOrBool; // string | number +>strOrNum = strOrNumOrBool : string | number +>strOrNum : string | number +>strOrNumOrBool : string | number +} +else { + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +// !(typeguard1 && typeguard2) +if (!(typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number")) { +>!(typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number") : boolean +>(typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number") : boolean +>typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number" : boolean +>typeof strOrNumOrBool !== "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>typeof strOrNumOrBool !== "number" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : number | boolean + + strOrNum = strOrNumOrBool; // string | number +>strOrNum = strOrNumOrBool : string | number +>strOrNum : string | number +>strOrNumOrBool : string | number +} +else { + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +// !(typeguard1) && !(typeguard2) +if (!(typeof strOrNumOrBool === "string") && !(typeof strOrNumOrBool === "number")) { +>!(typeof strOrNumOrBool === "string") && !(typeof strOrNumOrBool === "number") : boolean +>!(typeof strOrNumOrBool === "string") : boolean +>(typeof strOrNumOrBool === "string") : boolean +>typeof strOrNumOrBool === "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>!(typeof strOrNumOrBool === "number") : boolean +>(typeof strOrNumOrBool === "number") : boolean +>typeof strOrNumOrBool === "number" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : number | boolean + + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +>strOrNum = strOrNumOrBool : string | number +>strOrNum : string | number +>strOrNumOrBool : string | number +} +// !(typeguard1) && simpleExpr +if (!(typeof strOrNumOrBool === "string") && numOrBool !== strOrNumOrBool) { +>!(typeof strOrNumOrBool === "string") && numOrBool !== strOrNumOrBool : boolean +>!(typeof strOrNumOrBool === "string") : boolean +>(typeof strOrNumOrBool === "string") : boolean +>typeof strOrNumOrBool === "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean +>numOrBool !== strOrNumOrBool : boolean +>numOrBool : number | boolean +>strOrNumOrBool : number | boolean + + numOrBool = strOrNumOrBool; // number | boolean +>numOrBool = strOrNumOrBool : number | boolean +>numOrBool : number | boolean +>strOrNumOrBool : number | boolean +} +else { + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +>r1 : string | number | boolean +>strOrNumOrBool : string | number | boolean +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.js b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.js new file mode 100644 index 00000000000..4b675ffd65a --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.js @@ -0,0 +1,169 @@ +//// [typeGuardOfFormTypeOfBoolean.ts] +class C { private p: string }; + +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrNumOrBool: string | number | boolean; +var strOrC: string | C; +var numOrC: number | C; +var boolOrC: boolean | C; +var c: C; + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "boolean") { + bool = strOrNum; // boolean +} +else { + var z: string | number = strOrNum; // string | number +} +if (typeof strOrBool === "boolean") { + bool = strOrBool; // boolean +} +else { + str = strOrBool; // string +} +if (typeof numOrBool === "boolean") { + bool = numOrBool; // boolean +} +else { + num = numOrBool; // number +} +if (typeof strOrNumOrBool === "boolean") { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +if (typeof boolOrC === "boolean") { + bool = boolOrC; // boolean +} +else { + c = boolOrC; // C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "boolean") { + var z: string | number = strOrNum; // string | number +} +else { + bool = strOrNum; // boolean +} +if (typeof strOrBool !== "boolean") { + str = strOrBool; // string +} +else { + bool = strOrBool; // boolean +} +if (typeof numOrBool !== "boolean") { + num = numOrBool; // number +} +else { + bool = numOrBool; // boolean +} +if (typeof strOrNumOrBool !== "boolean") { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +if (typeof boolOrC !== "boolean") { + c = boolOrC; // C +} +else { + bool = boolOrC; // boolean +} + +//// [typeGuardOfFormTypeOfBoolean.js] +var C = (function () { + function C() { + } + return C; +})(); +; +var str; +var bool; +var num; +var strOrNum; +var strOrBool; +var numOrBool; +var strOrNumOrBool; +var strOrC; +var numOrC; +var boolOrC; +var c; +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "boolean") { + bool = strOrNum; // boolean +} +else { + var z = strOrNum; // string | number +} +if (typeof strOrBool === "boolean") { + bool = strOrBool; // boolean +} +else { + str = strOrBool; // string +} +if (typeof numOrBool === "boolean") { + bool = numOrBool; // boolean +} +else { + num = numOrBool; // number +} +if (typeof strOrNumOrBool === "boolean") { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +if (typeof boolOrC === "boolean") { + bool = boolOrC; // boolean +} +else { + c = boolOrC; // C +} +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "boolean") { + var z = strOrNum; // string | number +} +else { + bool = strOrNum; // boolean +} +if (typeof strOrBool !== "boolean") { + str = strOrBool; // string +} +else { + bool = strOrBool; // boolean +} +if (typeof numOrBool !== "boolean") { + num = numOrBool; // number +} +else { + bool = numOrBool; // boolean +} +if (typeof strOrNumOrBool !== "boolean") { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +if (typeof boolOrC !== "boolean") { + c = boolOrC; // C +} +else { + bool = boolOrC; // boolean +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types new file mode 100644 index 00000000000..6dc9792768c --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types @@ -0,0 +1,208 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfBoolean.ts === +class C { private p: string }; +>C : C +>p : string + +var str: string; +>str : string + +var bool: boolean; +>bool : boolean + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrBool: string | boolean; +>strOrBool : string | boolean + +var numOrBool: number | boolean +>numOrBool : number | boolean + +var strOrNumOrBool: string | number | boolean; +>strOrNumOrBool : string | number | boolean + +var strOrC: string | C; +>strOrC : string | C +>C : C + +var numOrC: number | C; +>numOrC : number | C +>C : C + +var boolOrC: boolean | C; +>boolOrC : boolean | C +>C : C + +var c: C; +>c : C +>C : C + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "boolean") { +>typeof strOrNum === "boolean" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + bool = strOrNum; // boolean +>bool = strOrNum : boolean +>bool : boolean +>strOrNum : boolean +} +else { + var z: string | number = strOrNum; // string | number +>z : string | number +>strOrNum : string | number +} +if (typeof strOrBool === "boolean") { +>typeof strOrBool === "boolean" : boolean +>typeof strOrBool : string +>strOrBool : string | boolean + + bool = strOrBool; // boolean +>bool = strOrBool : boolean +>bool : boolean +>strOrBool : boolean +} +else { + str = strOrBool; // string +>str = strOrBool : string +>str : string +>strOrBool : string +} +if (typeof numOrBool === "boolean") { +>typeof numOrBool === "boolean" : boolean +>typeof numOrBool : string +>numOrBool : number | boolean + + bool = numOrBool; // boolean +>bool = numOrBool : boolean +>bool : boolean +>numOrBool : boolean +} +else { + num = numOrBool; // number +>num = numOrBool : number +>num : number +>numOrBool : number +} +if (typeof strOrNumOrBool === "boolean") { +>typeof strOrNumOrBool === "boolean" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +>strOrNum = strOrNumOrBool : string | number +>strOrNum : string | number +>strOrNumOrBool : string | number +} +if (typeof boolOrC === "boolean") { +>typeof boolOrC === "boolean" : boolean +>typeof boolOrC : string +>boolOrC : boolean | C + + bool = boolOrC; // boolean +>bool = boolOrC : boolean +>bool : boolean +>boolOrC : boolean +} +else { + c = boolOrC; // C +>c = boolOrC : C +>c : C +>boolOrC : C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "boolean") { +>typeof strOrNum !== "boolean" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + var z: string | number = strOrNum; // string | number +>z : string | number +>strOrNum : string | number +} +else { + bool = strOrNum; // boolean +>bool = strOrNum : boolean +>bool : boolean +>strOrNum : boolean +} +if (typeof strOrBool !== "boolean") { +>typeof strOrBool !== "boolean" : boolean +>typeof strOrBool : string +>strOrBool : string | boolean + + str = strOrBool; // string +>str = strOrBool : string +>str : string +>strOrBool : string +} +else { + bool = strOrBool; // boolean +>bool = strOrBool : boolean +>bool : boolean +>strOrBool : boolean +} +if (typeof numOrBool !== "boolean") { +>typeof numOrBool !== "boolean" : boolean +>typeof numOrBool : string +>numOrBool : number | boolean + + num = numOrBool; // number +>num = numOrBool : number +>num : number +>numOrBool : number +} +else { + bool = numOrBool; // boolean +>bool = numOrBool : boolean +>bool : boolean +>numOrBool : boolean +} +if (typeof strOrNumOrBool !== "boolean") { +>typeof strOrNumOrBool !== "boolean" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + strOrNum = strOrNumOrBool; // string | number +>strOrNum = strOrNumOrBool : string | number +>strOrNum : string | number +>strOrNumOrBool : string | number +} +else { + bool = strOrNumOrBool; // boolean +>bool = strOrNumOrBool : boolean +>bool : boolean +>strOrNumOrBool : boolean +} +if (typeof boolOrC !== "boolean") { +>typeof boolOrC !== "boolean" : boolean +>typeof boolOrC : string +>boolOrC : boolean | C + + c = boolOrC; // C +>c = boolOrC : C +>c : C +>boolOrC : C +} +else { + bool = boolOrC; // boolean +>bool = boolOrC : boolean +>bool : boolean +>boolOrC : boolean +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfEqualEqualHasNoEffect.js b/tests/baselines/reference/typeGuardOfFormTypeOfEqualEqualHasNoEffect.js new file mode 100644 index 00000000000..0e7ada149df --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfEqualEqualHasNoEffect.js @@ -0,0 +1,73 @@ +//// [typeGuardOfFormTypeOfEqualEqualHasNoEffect.ts] +class C { private p: string }; + +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrC: string | C; + +// typeof x == s has not effect on typeguard +if (typeof strOrNum == "string") { + var r1 = strOrNum; // string | number +} +else { + var r1 = strOrNum; // string | number +} + +if (typeof strOrBool == "boolean") { + var r2 = strOrBool; // string | boolean +} +else { + var r2 = strOrBool; // string | boolean +} + +if (typeof numOrBool == "number") { + var r3 = numOrBool; // number | boolean +} +else { + var r3 = numOrBool; // number | boolean +} + +if (typeof strOrC == "Object") { + var r4 = strOrC; // string | C +} +else { + var r4 = strOrC; // string | C +} + +//// [typeGuardOfFormTypeOfEqualEqualHasNoEffect.js] +var C = (function () { + function C() { + } + return C; +})(); +; +var strOrNum; +var strOrBool; +var numOrBool; +var strOrC; +// typeof x == s has not effect on typeguard +if (typeof strOrNum == "string") { + var r1 = strOrNum; // string | number +} +else { + var r1 = strOrNum; // string | number +} +if (typeof strOrBool == "boolean") { + var r2 = strOrBool; // string | boolean +} +else { + var r2 = strOrBool; // string | boolean +} +if (typeof numOrBool == "number") { + var r3 = numOrBool; // number | boolean +} +else { + var r3 = numOrBool; // number | boolean +} +if (typeof strOrC == "Object") { + var r4 = strOrC; // string | C +} +else { + var r4 = strOrC; // string | C +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfEqualEqualHasNoEffect.types b/tests/baselines/reference/typeGuardOfFormTypeOfEqualEqualHasNoEffect.types new file mode 100644 index 00000000000..14d4cd61c51 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfEqualEqualHasNoEffect.types @@ -0,0 +1,78 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfEqualEqualHasNoEffect.ts === +class C { private p: string }; +>C : C +>p : string + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrBool: string | boolean; +>strOrBool : string | boolean + +var numOrBool: number | boolean +>numOrBool : number | boolean + +var strOrC: string | C; +>strOrC : string | C +>C : C + +// typeof x == s has not effect on typeguard +if (typeof strOrNum == "string") { +>typeof strOrNum == "string" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + var r1 = strOrNum; // string | number +>r1 : string | number +>strOrNum : string | number +} +else { + var r1 = strOrNum; // string | number +>r1 : string | number +>strOrNum : string | number +} + +if (typeof strOrBool == "boolean") { +>typeof strOrBool == "boolean" : boolean +>typeof strOrBool : string +>strOrBool : string | boolean + + var r2 = strOrBool; // string | boolean +>r2 : string | boolean +>strOrBool : string | boolean +} +else { + var r2 = strOrBool; // string | boolean +>r2 : string | boolean +>strOrBool : string | boolean +} + +if (typeof numOrBool == "number") { +>typeof numOrBool == "number" : boolean +>typeof numOrBool : string +>numOrBool : number | boolean + + var r3 = numOrBool; // number | boolean +>r3 : number | boolean +>numOrBool : number | boolean +} +else { + var r3 = numOrBool; // number | boolean +>r3 : number | boolean +>numOrBool : number | boolean +} + +if (typeof strOrC == "Object") { +>typeof strOrC == "Object" : boolean +>typeof strOrC : string +>strOrC : string | C + + var r4 = strOrC; // string | C +>r4 : string | C +>strOrC : string | C +} +else { + var r4 = strOrC; // string | C +>r4 : string | C +>strOrC : string | C +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfNotEqualHasNoEffect.js b/tests/baselines/reference/typeGuardOfFormTypeOfNotEqualHasNoEffect.js new file mode 100644 index 00000000000..6a9851b51ab --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfNotEqualHasNoEffect.js @@ -0,0 +1,73 @@ +//// [typeGuardOfFormTypeOfNotEqualHasNoEffect.ts] +class C { private p: string }; + +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrC: string | C; + +// typeof x != s has not effect on typeguard +if (typeof strOrNum != "string") { + var r1 = strOrNum; // string | number +} +else { + var r1 = strOrNum; // string | number +} + +if (typeof strOrBool != "boolean") { + var r2 = strOrBool; // string | boolean +} +else { + var r2 = strOrBool; // string | boolean +} + +if (typeof numOrBool != "number") { + var r3 = numOrBool; // number | boolean +} +else { + var r3 = numOrBool; // number | boolean +} + +if (typeof strOrC != "Object") { + var r4 = strOrC; // string | C +} +else { + var r4 = strOrC; // string | C +} + +//// [typeGuardOfFormTypeOfNotEqualHasNoEffect.js] +var C = (function () { + function C() { + } + return C; +})(); +; +var strOrNum; +var strOrBool; +var numOrBool; +var strOrC; +// typeof x != s has not effect on typeguard +if (typeof strOrNum != "string") { + var r1 = strOrNum; // string | number +} +else { + var r1 = strOrNum; // string | number +} +if (typeof strOrBool != "boolean") { + var r2 = strOrBool; // string | boolean +} +else { + var r2 = strOrBool; // string | boolean +} +if (typeof numOrBool != "number") { + var r3 = numOrBool; // number | boolean +} +else { + var r3 = numOrBool; // number | boolean +} +if (typeof strOrC != "Object") { + var r4 = strOrC; // string | C +} +else { + var r4 = strOrC; // string | C +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfNotEqualHasNoEffect.types b/tests/baselines/reference/typeGuardOfFormTypeOfNotEqualHasNoEffect.types new file mode 100644 index 00000000000..322799f6f7a --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfNotEqualHasNoEffect.types @@ -0,0 +1,78 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNotEqualHasNoEffect.ts === +class C { private p: string }; +>C : C +>p : string + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrBool: string | boolean; +>strOrBool : string | boolean + +var numOrBool: number | boolean +>numOrBool : number | boolean + +var strOrC: string | C; +>strOrC : string | C +>C : C + +// typeof x != s has not effect on typeguard +if (typeof strOrNum != "string") { +>typeof strOrNum != "string" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + var r1 = strOrNum; // string | number +>r1 : string | number +>strOrNum : string | number +} +else { + var r1 = strOrNum; // string | number +>r1 : string | number +>strOrNum : string | number +} + +if (typeof strOrBool != "boolean") { +>typeof strOrBool != "boolean" : boolean +>typeof strOrBool : string +>strOrBool : string | boolean + + var r2 = strOrBool; // string | boolean +>r2 : string | boolean +>strOrBool : string | boolean +} +else { + var r2 = strOrBool; // string | boolean +>r2 : string | boolean +>strOrBool : string | boolean +} + +if (typeof numOrBool != "number") { +>typeof numOrBool != "number" : boolean +>typeof numOrBool : string +>numOrBool : number | boolean + + var r3 = numOrBool; // number | boolean +>r3 : number | boolean +>numOrBool : number | boolean +} +else { + var r3 = numOrBool; // number | boolean +>r3 : number | boolean +>numOrBool : number | boolean +} + +if (typeof strOrC != "Object") { +>typeof strOrC != "Object" : boolean +>typeof strOrC : string +>strOrC : string | C + + var r4 = strOrC; // string | C +>r4 : string | C +>strOrC : string | C +} +else { + var r4 = strOrC; // string | C +>r4 : string | C +>strOrC : string | C +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.js b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.js new file mode 100644 index 00000000000..ff3a43430ba --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.js @@ -0,0 +1,169 @@ +//// [typeGuardOfFormTypeOfNumber.ts] +class C { private p: string }; + +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrNumOrBool: string | number | boolean; +var strOrC: string | C; +var numOrC: number | C; +var boolOrC: boolean | C; +var c: C; + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "number") { + num = strOrNum; // number +} +else { + str === strOrNum; // string +} +if (typeof strOrBool === "number") { + num = strOrBool; // number +} +else { + var y: string | boolean = strOrBool; // string | boolean +} +if (typeof numOrBool === "number") { + num = numOrBool; // number +} +else { + var x: number | boolean = numOrBool; // number | boolean +} +if (typeof strOrNumOrBool === "number") { + num = strOrNumOrBool; // number +} +else { + strOrBool = strOrNumOrBool; // string | boolean +} +if (typeof numOrC === "number") { + num = numOrC; // number +} +else { + c = numOrC; // C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "number") { + str === strOrNum; // string +} +else { + num = strOrNum; // number +} +if (typeof strOrBool !== "number") { + var y: string | boolean = strOrBool; // string | boolean +} +else { + num = strOrBool; // number +} +if (typeof numOrBool !== "number") { + var x: number | boolean = numOrBool; // number | boolean +} +else { + num = numOrBool; // number +} +if (typeof strOrNumOrBool !== "number") { + strOrBool = strOrNumOrBool; // string | boolean +} +else { + num = strOrNumOrBool; // number +} +if (typeof numOrC !== "number") { + c = numOrC; // C +} +else { + num = numOrC; // number +} + +//// [typeGuardOfFormTypeOfNumber.js] +var C = (function () { + function C() { + } + return C; +})(); +; +var str; +var bool; +var num; +var strOrNum; +var strOrBool; +var numOrBool; +var strOrNumOrBool; +var strOrC; +var numOrC; +var boolOrC; +var c; +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "number") { + num = strOrNum; // number +} +else { + str === strOrNum; // string +} +if (typeof strOrBool === "number") { + num = strOrBool; // number +} +else { + var y = strOrBool; // string | boolean +} +if (typeof numOrBool === "number") { + num = numOrBool; // number +} +else { + var x = numOrBool; // number | boolean +} +if (typeof strOrNumOrBool === "number") { + num = strOrNumOrBool; // number +} +else { + strOrBool = strOrNumOrBool; // string | boolean +} +if (typeof numOrC === "number") { + num = numOrC; // number +} +else { + c = numOrC; // C +} +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "number") { + str === strOrNum; // string +} +else { + num = strOrNum; // number +} +if (typeof strOrBool !== "number") { + var y = strOrBool; // string | boolean +} +else { + num = strOrBool; // number +} +if (typeof numOrBool !== "number") { + var x = numOrBool; // number | boolean +} +else { + num = numOrBool; // number +} +if (typeof strOrNumOrBool !== "number") { + strOrBool = strOrNumOrBool; // string | boolean +} +else { + num = strOrNumOrBool; // number +} +if (typeof numOrC !== "number") { + c = numOrC; // C +} +else { + num = numOrC; // number +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types new file mode 100644 index 00000000000..7fd8855ec92 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types @@ -0,0 +1,206 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNumber.ts === +class C { private p: string }; +>C : C +>p : string + +var str: string; +>str : string + +var bool: boolean; +>bool : boolean + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrBool: string | boolean; +>strOrBool : string | boolean + +var numOrBool: number | boolean +>numOrBool : number | boolean + +var strOrNumOrBool: string | number | boolean; +>strOrNumOrBool : string | number | boolean + +var strOrC: string | C; +>strOrC : string | C +>C : C + +var numOrC: number | C; +>numOrC : number | C +>C : C + +var boolOrC: boolean | C; +>boolOrC : boolean | C +>C : C + +var c: C; +>c : C +>C : C + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "number") { +>typeof strOrNum === "number" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + num = strOrNum; // number +>num = strOrNum : number +>num : number +>strOrNum : number +} +else { + str === strOrNum; // string +>str === strOrNum : boolean +>str : string +>strOrNum : string +} +if (typeof strOrBool === "number") { +>typeof strOrBool === "number" : boolean +>typeof strOrBool : string +>strOrBool : string | boolean + + num = strOrBool; // number +>num = strOrBool : number +>num : number +>strOrBool : number +} +else { + var y: string | boolean = strOrBool; // string | boolean +>y : string | boolean +>strOrBool : string | boolean +} +if (typeof numOrBool === "number") { +>typeof numOrBool === "number" : boolean +>typeof numOrBool : string +>numOrBool : number | boolean + + num = numOrBool; // number +>num = numOrBool : number +>num : number +>numOrBool : number +} +else { + var x: number | boolean = numOrBool; // number | boolean +>x : number | boolean +>numOrBool : boolean +} +if (typeof strOrNumOrBool === "number") { +>typeof strOrNumOrBool === "number" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + num = strOrNumOrBool; // number +>num = strOrNumOrBool : number +>num : number +>strOrNumOrBool : number +} +else { + strOrBool = strOrNumOrBool; // string | boolean +>strOrBool = strOrNumOrBool : string | boolean +>strOrBool : string | boolean +>strOrNumOrBool : string | boolean +} +if (typeof numOrC === "number") { +>typeof numOrC === "number" : boolean +>typeof numOrC : string +>numOrC : number | C + + num = numOrC; // number +>num = numOrC : number +>num : number +>numOrC : number +} +else { + c = numOrC; // C +>c = numOrC : C +>c : C +>numOrC : C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "number") { +>typeof strOrNum !== "number" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + str === strOrNum; // string +>str === strOrNum : boolean +>str : string +>strOrNum : string +} +else { + num = strOrNum; // number +>num = strOrNum : number +>num : number +>strOrNum : number +} +if (typeof strOrBool !== "number") { +>typeof strOrBool !== "number" : boolean +>typeof strOrBool : string +>strOrBool : string | boolean + + var y: string | boolean = strOrBool; // string | boolean +>y : string | boolean +>strOrBool : string | boolean +} +else { + num = strOrBool; // number +>num = strOrBool : number +>num : number +>strOrBool : number +} +if (typeof numOrBool !== "number") { +>typeof numOrBool !== "number" : boolean +>typeof numOrBool : string +>numOrBool : number | boolean + + var x: number | boolean = numOrBool; // number | boolean +>x : number | boolean +>numOrBool : boolean +} +else { + num = numOrBool; // number +>num = numOrBool : number +>num : number +>numOrBool : number +} +if (typeof strOrNumOrBool !== "number") { +>typeof strOrNumOrBool !== "number" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + strOrBool = strOrNumOrBool; // string | boolean +>strOrBool = strOrNumOrBool : string | boolean +>strOrBool : string | boolean +>strOrNumOrBool : string | boolean +} +else { + num = strOrNumOrBool; // number +>num = strOrNumOrBool : number +>num : number +>strOrNumOrBool : number +} +if (typeof numOrC !== "number") { +>typeof numOrC !== "number" : boolean +>typeof numOrC : string +>numOrC : number | C + + c = numOrC; // C +>c = numOrC : C +>c : C +>numOrC : C +} +else { + num = numOrC; // number +>num = numOrC : number +>num : number +>numOrC : number +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.js b/tests/baselines/reference/typeGuardOfFormTypeOfOther.js new file mode 100644 index 00000000000..155b2b9ce57 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.js @@ -0,0 +1,148 @@ +//// [typeGuardOfFormTypeOfOther.ts] +class C { private p: string }; + +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrNumOrBool: string | number | boolean; +var strOrC: string | C; +var numOrC: number | C; +var boolOrC: boolean | C; +var emptyObj: {}; +var c: C; + +// A type guard of the form typeof x === s, +// where s is a string literal with any value but 'string', 'number' or 'boolean', +// - when true, removes the primitive types string, number, and boolean from the type of x, or +// - when false, has no effect on the type of x. + +if (typeof strOrNumOrBool === "Object") { + emptyObj = strOrNumOrBool; // {} +} +else { + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +} +if (typeof strOrC === "Object") { + c = strOrC; // C +} +else { + var r2: string | C = strOrC; // string | C +} +if (typeof numOrC === "Object") { + c = numOrC; // C +} +else { + var r3: number | C = numOrC; // number | C +} +if (typeof boolOrC === "Object") { + c = boolOrC; // C +} +else { + var r4: boolean | C = boolOrC; // boolean | C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNumOrBool !== "Object") { + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +} +else { + emptyObj = strOrNumOrBool; // {} +} +if (typeof strOrC !== "Object") { + var r2: string | C = strOrC; // string | C +} +else { + c = strOrC; // C +} +if (typeof numOrC !== "Object") { + var r3: number | C = numOrC; // number | C +} +else { + c = numOrC; // C +} +if (typeof boolOrC !== "Object") { + var r4: boolean | C = boolOrC; // boolean | C +} +else { + c = boolOrC; // C +} + +//// [typeGuardOfFormTypeOfOther.js] +var C = (function () { + function C() { + } + return C; +})(); +; +var str; +var bool; +var num; +var strOrNum; +var strOrBool; +var numOrBool; +var strOrNumOrBool; +var strOrC; +var numOrC; +var boolOrC; +var emptyObj; +var c; +// A type guard of the form typeof x === s, +// where s is a string literal with any value but 'string', 'number' or 'boolean', +// - when true, removes the primitive types string, number, and boolean from the type of x, or +// - when false, has no effect on the type of x. +if (typeof strOrNumOrBool === "Object") { + emptyObj = strOrNumOrBool; // {} +} +else { + var r1 = strOrNumOrBool; // string | number | boolean +} +if (typeof strOrC === "Object") { + c = strOrC; // C +} +else { + var r2 = strOrC; // string | C +} +if (typeof numOrC === "Object") { + c = numOrC; // C +} +else { + var r3 = numOrC; // number | C +} +if (typeof boolOrC === "Object") { + c = boolOrC; // C +} +else { + var r4 = boolOrC; // boolean | C +} +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNumOrBool !== "Object") { + var r1 = strOrNumOrBool; // string | number | boolean +} +else { + emptyObj = strOrNumOrBool; // {} +} +if (typeof strOrC !== "Object") { + var r2 = strOrC; // string | C +} +else { + c = strOrC; // C +} +if (typeof numOrC !== "Object") { + var r3 = numOrC; // number | C +} +else { + c = numOrC; // C +} +if (typeof boolOrC !== "Object") { + var r4 = boolOrC; // boolean | C +} +else { + c = boolOrC; // C +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.types b/tests/baselines/reference/typeGuardOfFormTypeOfOther.types new file mode 100644 index 00000000000..729c255b147 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.types @@ -0,0 +1,180 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts === +class C { private p: string }; +>C : C +>p : string + +var str: string; +>str : string + +var bool: boolean; +>bool : boolean + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrBool: string | boolean; +>strOrBool : string | boolean + +var numOrBool: number | boolean +>numOrBool : number | boolean + +var strOrNumOrBool: string | number | boolean; +>strOrNumOrBool : string | number | boolean + +var strOrC: string | C; +>strOrC : string | C +>C : C + +var numOrC: number | C; +>numOrC : number | C +>C : C + +var boolOrC: boolean | C; +>boolOrC : boolean | C +>C : C + +var emptyObj: {}; +>emptyObj : {} + +var c: C; +>c : C +>C : C + +// A type guard of the form typeof x === s, +// where s is a string literal with any value but 'string', 'number' or 'boolean', +// - when true, removes the primitive types string, number, and boolean from the type of x, or +// - when false, has no effect on the type of x. + +if (typeof strOrNumOrBool === "Object") { +>typeof strOrNumOrBool === "Object" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + emptyObj = strOrNumOrBool; // {} +>emptyObj = strOrNumOrBool : {} +>emptyObj : {} +>strOrNumOrBool : {} +} +else { + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +>r1 : string | number | boolean +>strOrNumOrBool : string | number | boolean +} +if (typeof strOrC === "Object") { +>typeof strOrC === "Object" : boolean +>typeof strOrC : string +>strOrC : string | C + + c = strOrC; // C +>c = strOrC : C +>c : C +>strOrC : C +} +else { + var r2: string | C = strOrC; // string | C +>r2 : string | C +>C : C +>strOrC : string | C +} +if (typeof numOrC === "Object") { +>typeof numOrC === "Object" : boolean +>typeof numOrC : string +>numOrC : number | C + + c = numOrC; // C +>c = numOrC : C +>c : C +>numOrC : C +} +else { + var r3: number | C = numOrC; // number | C +>r3 : number | C +>C : C +>numOrC : number | C +} +if (typeof boolOrC === "Object") { +>typeof boolOrC === "Object" : boolean +>typeof boolOrC : string +>boolOrC : boolean | C + + c = boolOrC; // C +>c = boolOrC : C +>c : C +>boolOrC : C +} +else { + var r4: boolean | C = boolOrC; // boolean | C +>r4 : boolean | C +>C : C +>boolOrC : boolean | C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNumOrBool !== "Object") { +>typeof strOrNumOrBool !== "Object" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +>r1 : string | number | boolean +>strOrNumOrBool : string | number | boolean +} +else { + emptyObj = strOrNumOrBool; // {} +>emptyObj = strOrNumOrBool : {} +>emptyObj : {} +>strOrNumOrBool : {} +} +if (typeof strOrC !== "Object") { +>typeof strOrC !== "Object" : boolean +>typeof strOrC : string +>strOrC : string | C + + var r2: string | C = strOrC; // string | C +>r2 : string | C +>C : C +>strOrC : string | C +} +else { + c = strOrC; // C +>c = strOrC : C +>c : C +>strOrC : C +} +if (typeof numOrC !== "Object") { +>typeof numOrC !== "Object" : boolean +>typeof numOrC : string +>numOrC : number | C + + var r3: number | C = numOrC; // number | C +>r3 : number | C +>C : C +>numOrC : number | C +} +else { + c = numOrC; // C +>c = numOrC : C +>c : C +>numOrC : C +} +if (typeof boolOrC !== "Object") { +>typeof boolOrC !== "Object" : boolean +>typeof boolOrC : string +>boolOrC : boolean | C + + var r4: boolean | C = boolOrC; // boolean | C +>r4 : boolean | C +>C : C +>boolOrC : boolean | C +} +else { + c = boolOrC; // C +>c = boolOrC : C +>c : C +>boolOrC : C +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfString.js b/tests/baselines/reference/typeGuardOfFormTypeOfString.js new file mode 100644 index 00000000000..311f499e171 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfString.js @@ -0,0 +1,169 @@ +//// [typeGuardOfFormTypeOfString.ts] +class C { private p: string }; + +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrNumOrBool: string | number | boolean; +var strOrC: string | C; +var numOrC: number | C; +var boolOrC: boolean | C; +var c: C; + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "string") { + str = strOrNum; // string +} +else { + num === strOrNum; // number +} +if (typeof strOrBool === "string") { + str = strOrBool; // string +} +else { + bool = strOrBool; // boolean +} +if (typeof numOrBool === "string") { + str = numOrBool; // string +} +else { + var x : number | boolean = numOrBool; // number | boolean +} +if (typeof strOrNumOrBool === "string") { + str = strOrNumOrBool; // string +} +else { + numOrBool = strOrNumOrBool; // number | boolean +} +if (typeof strOrC === "string") { + str = strOrC; // string +} +else { + c = strOrC; // C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "string") { + num === strOrNum; // number +} +else { + str = strOrNum; // string +} +if (typeof strOrBool !== "string") { + bool = strOrBool; // boolean +} +else { + str = strOrBool; // string +} +if (typeof numOrBool !== "string") { + var x: number | boolean = numOrBool; // number | boolean +} +else { + str = numOrBool; // string +} +if (typeof strOrNumOrBool !== "string") { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + str = strOrNumOrBool; // string +} +if (typeof strOrC !== "string") { + c = strOrC; // C +} +else { + str = strOrC; // string +} + +//// [typeGuardOfFormTypeOfString.js] +var C = (function () { + function C() { + } + return C; +})(); +; +var str; +var bool; +var num; +var strOrNum; +var strOrBool; +var numOrBool; +var strOrNumOrBool; +var strOrC; +var numOrC; +var boolOrC; +var c; +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "string") { + str = strOrNum; // string +} +else { + num === strOrNum; // number +} +if (typeof strOrBool === "string") { + str = strOrBool; // string +} +else { + bool = strOrBool; // boolean +} +if (typeof numOrBool === "string") { + str = numOrBool; // string +} +else { + var x = numOrBool; // number | boolean +} +if (typeof strOrNumOrBool === "string") { + str = strOrNumOrBool; // string +} +else { + numOrBool = strOrNumOrBool; // number | boolean +} +if (typeof strOrC === "string") { + str = strOrC; // string +} +else { + c = strOrC; // C +} +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "string") { + num === strOrNum; // number +} +else { + str = strOrNum; // string +} +if (typeof strOrBool !== "string") { + bool = strOrBool; // boolean +} +else { + str = strOrBool; // string +} +if (typeof numOrBool !== "string") { + var x = numOrBool; // number | boolean +} +else { + str = numOrBool; // string +} +if (typeof strOrNumOrBool !== "string") { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + str = strOrNumOrBool; // string +} +if (typeof strOrC !== "string") { + c = strOrC; // C +} +else { + str = strOrC; // string +} diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfString.types b/tests/baselines/reference/typeGuardOfFormTypeOfString.types new file mode 100644 index 00000000000..e6d90b74c66 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormTypeOfString.types @@ -0,0 +1,208 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfString.ts === +class C { private p: string }; +>C : C +>p : string + +var str: string; +>str : string + +var bool: boolean; +>bool : boolean + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var strOrBool: string | boolean; +>strOrBool : string | boolean + +var numOrBool: number | boolean +>numOrBool : number | boolean + +var strOrNumOrBool: string | number | boolean; +>strOrNumOrBool : string | number | boolean + +var strOrC: string | C; +>strOrC : string | C +>C : C + +var numOrC: number | C; +>numOrC : number | C +>C : C + +var boolOrC: boolean | C; +>boolOrC : boolean | C +>C : C + +var c: C; +>c : C +>C : C + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "string") { +>typeof strOrNum === "string" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + str = strOrNum; // string +>str = strOrNum : string +>str : string +>strOrNum : string +} +else { + num === strOrNum; // number +>num === strOrNum : boolean +>num : number +>strOrNum : number +} +if (typeof strOrBool === "string") { +>typeof strOrBool === "string" : boolean +>typeof strOrBool : string +>strOrBool : string | boolean + + str = strOrBool; // string +>str = strOrBool : string +>str : string +>strOrBool : string +} +else { + bool = strOrBool; // boolean +>bool = strOrBool : boolean +>bool : boolean +>strOrBool : boolean +} +if (typeof numOrBool === "string") { +>typeof numOrBool === "string" : boolean +>typeof numOrBool : string +>numOrBool : number | boolean + + str = numOrBool; // string +>str = numOrBool : string +>str : string +>numOrBool : string +} +else { + var x : number | boolean = numOrBool; // number | boolean +>x : number | boolean +>numOrBool : number | boolean +} +if (typeof strOrNumOrBool === "string") { +>typeof strOrNumOrBool === "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + str = strOrNumOrBool; // string +>str = strOrNumOrBool : string +>str : string +>strOrNumOrBool : string +} +else { + numOrBool = strOrNumOrBool; // number | boolean +>numOrBool = strOrNumOrBool : number | boolean +>numOrBool : number | boolean +>strOrNumOrBool : number | boolean +} +if (typeof strOrC === "string") { +>typeof strOrC === "string" : boolean +>typeof strOrC : string +>strOrC : string | C + + str = strOrC; // string +>str = strOrC : string +>str : string +>strOrC : string +} +else { + c = strOrC; // C +>c = strOrC : C +>c : C +>strOrC : C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "string") { +>typeof strOrNum !== "string" : boolean +>typeof strOrNum : string +>strOrNum : string | number + + num === strOrNum; // number +>num === strOrNum : boolean +>num : number +>strOrNum : number +} +else { + str = strOrNum; // string +>str = strOrNum : string +>str : string +>strOrNum : string +} +if (typeof strOrBool !== "string") { +>typeof strOrBool !== "string" : boolean +>typeof strOrBool : string +>strOrBool : string | boolean + + bool = strOrBool; // boolean +>bool = strOrBool : boolean +>bool : boolean +>strOrBool : boolean +} +else { + str = strOrBool; // string +>str = strOrBool : string +>str : string +>strOrBool : string +} +if (typeof numOrBool !== "string") { +>typeof numOrBool !== "string" : boolean +>typeof numOrBool : string +>numOrBool : number | boolean + + var x: number | boolean = numOrBool; // number | boolean +>x : number | boolean +>numOrBool : number | boolean +} +else { + str = numOrBool; // string +>str = numOrBool : string +>str : string +>numOrBool : string +} +if (typeof strOrNumOrBool !== "string") { +>typeof strOrNumOrBool !== "string" : boolean +>typeof strOrNumOrBool : string +>strOrNumOrBool : string | number | boolean + + numOrBool = strOrNumOrBool; // number | boolean +>numOrBool = strOrNumOrBool : number | boolean +>numOrBool : number | boolean +>strOrNumOrBool : number | boolean +} +else { + str = strOrNumOrBool; // string +>str = strOrNumOrBool : string +>str : string +>strOrNumOrBool : string +} +if (typeof strOrC !== "string") { +>typeof strOrC !== "string" : boolean +>typeof strOrC : string +>strOrC : string | C + + c = strOrC; // C +>c = strOrC : C +>c : C +>strOrC : C +} +else { + str = strOrC; // string +>str = strOrC : string +>str : string +>strOrC : string +} diff --git a/tests/baselines/reference/typeGuardsDefeat.js b/tests/baselines/reference/typeGuardsDefeat.js new file mode 100644 index 00000000000..b5e1e0c362c --- /dev/null +++ b/tests/baselines/reference/typeGuardsDefeat.js @@ -0,0 +1,75 @@ +//// [typeGuardsDefeat.ts] +// Also note that it is possible to defeat a type guard by calling a function that changes the +// type of the guarded variable. +function foo(x: number | string) { + function f() { + x = 10; + } + if (typeof x === "string") { + f(); + return x.length; // string + } + else { + return x++; // number + } +} +function foo2(x: number | string) { + if (typeof x === "string") { + return x.length; // string + } + else { + (function f() { + x = 10; + })(); + return x++; // number + } +} +function foo3(x: number | string) { + if (typeof x === "string") { + return x.length; // string + } + else { + (() => { + x = 10; + })(); + return x++; // number + } +} + +//// [typeGuardsDefeat.js] +// Also note that it is possible to defeat a type guard by calling a function that changes the +// type of the guarded variable. +function foo(x) { + function f() { + x = 10; + } + if (typeof x === "string") { + f(); + return x.length; // string + } + else { + return x++; // number + } +} +function foo2(x) { + if (typeof x === "string") { + return x.length; // string + } + else { + (function f() { + x = 10; + })(); + return x++; // number + } +} +function foo3(x) { + if (typeof x === "string") { + return x.length; // string + } + else { + (function () { + x = 10; + })(); + return x++; // number + } +} diff --git a/tests/baselines/reference/typeGuardsDefeat.types b/tests/baselines/reference/typeGuardsDefeat.types new file mode 100644 index 00000000000..6c4153cc3e2 --- /dev/null +++ b/tests/baselines/reference/typeGuardsDefeat.types @@ -0,0 +1,95 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts === +// Also note that it is possible to defeat a type guard by calling a function that changes the +// type of the guarded variable. +function foo(x: number | string) { +>foo : (x: string | number) => number +>x : string | number + + function f() { +>f : () => void + + x = 10; +>x = 10 : number +>x : string | number + } + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + f(); +>f() : void +>f : () => void + + return x.length; // string +>x.length : number +>x : string +>length : number + } + else { + return x++; // number +>x++ : number +>x : number + } +} +function foo2(x: number | string) { +>foo2 : (x: string | number) => number +>x : string | number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + return x.length; // string +>x.length : number +>x : string +>length : number + } + else { + (function f() { +>(function f() { x = 10; })() : void +>(function f() { x = 10; }) : () => void +>function f() { x = 10; } : () => void +>f : () => void + + x = 10; +>x = 10 : number +>x : string | number + + })(); + return x++; // number +>x++ : number +>x : number + } +} +function foo3(x: number | string) { +>foo3 : (x: string | number) => number +>x : string | number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + return x.length; // string +>x.length : number +>x : string +>length : number + } + else { + (() => { +>(() => { x = 10; })() : void +>(() => { x = 10; }) : () => void +>() => { x = 10; } : () => void + + x = 10; +>x = 10 : number +>x : string | number + + })(); + return x++; // number +>x++ : number +>x : number + } +} diff --git a/tests/baselines/reference/typeGuardsInClassAccessors.js b/tests/baselines/reference/typeGuardsInClassAccessors.js new file mode 100644 index 00000000000..8f487b72535 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInClassAccessors.js @@ -0,0 +1,209 @@ +//// [typeGuardsInClassAccessors.ts] + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var strOrNum: string | number; +var var1: string | number; +class ClassWithAccessors { + // Inside public accessor getter + get p1() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + } + // Inside public accessor setter + set p1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // parameter of function declaration + num = typeof param === "string" && param.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + } + // Inside private accessor getter + private get pp1() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + } + // Inside private accessor setter + private set pp1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // parameter of function declaration + num = typeof param === "string" && param.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + } + // Inside static accessor getter + static get s1() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + } + // Inside static accessor setter + static set s1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // parameter of function declaration + num = typeof param === "string" && param.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + } + // Inside private static accessor getter + private static get ss1() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + } + // Inside private static accessor setter + private static set ss1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // parameter of function declaration + num = typeof param === "string" && param.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + } +} + + +//// [typeGuardsInClassAccessors.js] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. +// variables in global +var num; +var strOrNum; +var var1; +var ClassWithAccessors = (function () { + function ClassWithAccessors() { + } + Object.defineProperty(ClassWithAccessors.prototype, "p1", { + // Inside public accessor getter + get: function () { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + return strOrNum; + }, + // Inside public accessor setter + set: function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // parameter of function declaration + num = typeof param === "string" && param.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ClassWithAccessors.prototype, "pp1", { + // Inside private accessor getter + get: function () { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + return strOrNum; + }, + // Inside private accessor setter + set: function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // parameter of function declaration + num = typeof param === "string" && param.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ClassWithAccessors, "s1", { + // Inside static accessor getter + get: function () { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + return strOrNum; + }, + // Inside static accessor setter + set: function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // parameter of function declaration + num = typeof param === "string" && param.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ClassWithAccessors, "ss1", { + // Inside private static accessor getter + get: function () { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + return strOrNum; + }, + // Inside private static accessor setter + set: function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // parameter of function declaration + num = typeof param === "string" && param.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + }, + enumerable: true, + configurable: true + }); + return ClassWithAccessors; +})(); diff --git a/tests/baselines/reference/typeGuardsInClassAccessors.types b/tests/baselines/reference/typeGuardsInClassAccessors.types new file mode 100644 index 00000000000..48ea732f3d7 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInClassAccessors.types @@ -0,0 +1,332 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInClassAccessors.ts === + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var var1: string | number; +>var1 : string | number + +class ClassWithAccessors { +>ClassWithAccessors : ClassWithAccessors + + // Inside public accessor getter + get p1() { +>p1 : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + return strOrNum; +>strOrNum : string | number + } + // Inside public accessor setter + set p1(param: string | number) { +>p1 : string | number +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // parameter of function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + } + // Inside private accessor getter + private get pp1() { +>pp1 : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + return strOrNum; +>strOrNum : string | number + } + // Inside private accessor setter + private set pp1(param: string | number) { +>pp1 : string | number +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // parameter of function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + } + // Inside static accessor getter + static get s1() { +>s1 : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + return strOrNum; +>strOrNum : string | number + } + // Inside static accessor setter + static set s1(param: string | number) { +>s1 : string | number +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // parameter of function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + } + // Inside private static accessor getter + private static get ss1() { +>ss1 : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + return strOrNum; +>strOrNum : string | number + } + // Inside private static accessor setter + private static set ss1(param: string | number) { +>ss1 : string | number +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // parameter of function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + } +} + diff --git a/tests/baselines/reference/typeGuardsInClassMethods.js b/tests/baselines/reference/typeGuardsInClassMethods.js new file mode 100644 index 00000000000..cbe6dfe161a --- /dev/null +++ b/tests/baselines/reference/typeGuardsInClassMethods.js @@ -0,0 +1,128 @@ +//// [typeGuardsInClassMethods.ts] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var var1: string | number; +class C1 { + constructor(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + private p1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + p2(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + private static s1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + static s2(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } +} + + +//// [typeGuardsInClassMethods.js] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. +// variables in global +var num; +var var1; +var C1 = (function () { + function C1(param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + C1.prototype.p1 = function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + // parameters in function declaration + num = typeof param === "string" && param.length; // string + }; + // Inside function declaration + C1.prototype.p2 = function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + // parameters in function declaration + num = typeof param === "string" && param.length; // string + }; + // Inside function declaration + C1.s1 = function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + // parameters in function declaration + num = typeof param === "string" && param.length; // string + }; + // Inside function declaration + C1.s2 = function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + // parameters in function declaration + num = typeof param === "string" && param.length; // string + }; + return C1; +})(); diff --git a/tests/baselines/reference/typeGuardsInClassMethods.types b/tests/baselines/reference/typeGuardsInClassMethods.types new file mode 100644 index 00000000000..fa44347cd5c --- /dev/null +++ b/tests/baselines/reference/typeGuardsInClassMethods.types @@ -0,0 +1,234 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInClassMethods.ts === +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +>num : number + +var var1: string | number; +>var1 : string | number + +class C1 { +>C1 : C1 + + constructor(param: string | number) { +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + } + // Inside function declaration + private p1(param: string | number) { +>p1 : (param: string | number) => void +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + } + // Inside function declaration + p2(param: string | number) { +>p2 : (param: string | number) => void +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + } + // Inside function declaration + private static s1(param: string | number) { +>s1 : (param: string | number) => void +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + } + // Inside function declaration + static s2(param: string | number) { +>s2 : (param: string | number) => void +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + } +} + diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.js b/tests/baselines/reference/typeGuardsInConditionalExpression.js new file mode 100644 index 00000000000..303a27dc938 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.js @@ -0,0 +1,160 @@ +//// [typeGuardsInConditionalExpression.ts] +// In the true expression of a conditional expression, +// the type of a variable or parameter is narrowed by any type guard in the condition when true, +// provided the true expression contains no assignments to the variable or parameter. +// In the false expression of a conditional expression, +// the type of a variable or parameter is narrowed by any type guard in the condition when false, +// provided the false expression contains no assignments to the variable or parameter. + +function foo(x: number | string) { + return typeof x === "string" + ? x.length // string + : x++; // number +} +function foo2(x: number | string) { + // x is assigned in the if true branch, the type is not narrowed + return typeof x === "string" + ? (x = 10 && x)// string | number + : x; // string | number +} +function foo3(x: number | string) { + // x is assigned in the if false branch, the type is not narrowed + // even though assigned using same type as narrowed expression + return typeof x === "string" + ? (x = "Hello" && x) // string | number + : x; // string | number +} +function foo4(x: number | string) { + // false branch updates the variable - so here it is not number + // even though assigned using same type as narrowed expression + return typeof x === "string" + ? x // string | number + : (x = 10 && x); // string | number +} +function foo5(x: number | string) { + // false branch updates the variable - so here it is not number + return typeof x === "string" + ? x // string | number + : (x = "hello" && x); // string | number +} +function foo6(x: number | string) { + // Modify in both branches + return typeof x === "string" + ? (x = 10 && x) // string | number + : (x = "hello" && x); // string | number +} +function foo7(x: number | string | boolean) { + return typeof x === "string" + ? x === "hello" // string + : typeof x === "boolean" + ? x // boolean + : x == 10; // number +} +function foo8(x: number | string | boolean) { + var b: number | boolean; + return typeof x === "string" + ? x === "hello" + : ((b = x) && // number | boolean + (typeof x === "boolean" + ? x // boolean + : x == 10)); // number +} +function foo9(x: number | string) { + var y = 10; + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + return typeof x === "string" + ? ((y = x.length) && x === "hello") // string + : x === 10; // number +} +function foo10(x: number | string | boolean) { + // Mixing typeguards + var b: boolean | number; + return typeof x === "string" + ? x // string + : ((b = x) // x is number | boolean + && typeof x === "number" + && x.toString()); // x is number +} +function foo11(x: number | string | boolean) { + // Mixing typeguards + // Assigning value to x deep inside another guard stops narrowing of type too + var b: number | boolean | string; + return typeof x === "string" + ? x // number | boolean | string - changed in the false branch + : ((b = x) // x is number | boolean | string - because the assignment changed it + && typeof x === "number" + && (x = 10) // assignment to x + && x); // x is number | boolean | string +} +function foo12(x: number | string | boolean) { + // Mixing typeguards + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + var b: number | boolean | string; + return typeof x === "string" + ? (x = 10 && x.toString().length) // number | boolean | string - changed here + : ((b = x) // x is number | boolean | string - changed in true branch + && typeof x === "number" + && x); // x is number +} + +//// [typeGuardsInConditionalExpression.js] +// In the true expression of a conditional expression, +// the type of a variable or parameter is narrowed by any type guard in the condition when true, +// provided the true expression contains no assignments to the variable or parameter. +// In the false expression of a conditional expression, +// the type of a variable or parameter is narrowed by any type guard in the condition when false, +// provided the false expression contains no assignments to the variable or parameter. +function foo(x) { + return typeof x === "string" ? x.length : x++; // number +} +function foo2(x) { + // x is assigned in the if true branch, the type is not narrowed + return typeof x === "string" ? (x = 10 && x) : x; // string | number +} +function foo3(x) { + // x is assigned in the if false branch, the type is not narrowed + // even though assigned using same type as narrowed expression + return typeof x === "string" ? (x = "Hello" && x) : x; // string | number +} +function foo4(x) { + // false branch updates the variable - so here it is not number + // even though assigned using same type as narrowed expression + return typeof x === "string" ? x : (x = 10 && x); // string | number +} +function foo5(x) { + // false branch updates the variable - so here it is not number + return typeof x === "string" ? x : (x = "hello" && x); // string | number +} +function foo6(x) { + // Modify in both branches + return typeof x === "string" ? (x = 10 && x) : (x = "hello" && x); // string | number +} +function foo7(x) { + return typeof x === "string" ? x === "hello" : typeof x === "boolean" ? x : x == 10; // number +} +function foo8(x) { + var b; + return typeof x === "string" ? x === "hello" : ((b = x) && (typeof x === "boolean" ? x : x == 10)); // number +} +function foo9(x) { + var y = 10; + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + return typeof x === "string" ? ((y = x.length) && x === "hello") : x === 10; // number +} +function foo10(x) { + // Mixing typeguards + var b; + return typeof x === "string" ? x : ((b = x) && typeof x === "number" && x.toString()); // x is number +} +function foo11(x) { + // Mixing typeguards + // Assigning value to x deep inside another guard stops narrowing of type too + var b; + return typeof x === "string" ? x : ((b = x) && typeof x === "number" && (x = 10) && x); // x is number | boolean | string +} +function foo12(x) { + // Mixing typeguards + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + var b; + return typeof x === "string" ? (x = 10 && x.toString().length) : ((b = x) && typeof x === "number" && x); // x is number +} diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.types b/tests/baselines/reference/typeGuardsInConditionalExpression.types new file mode 100644 index 00000000000..14b35480cd8 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.types @@ -0,0 +1,356 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts === +// In the true expression of a conditional expression, +// the type of a variable or parameter is narrowed by any type guard in the condition when true, +// provided the true expression contains no assignments to the variable or parameter. +// In the false expression of a conditional expression, +// the type of a variable or parameter is narrowed by any type guard in the condition when false, +// provided the false expression contains no assignments to the variable or parameter. + +function foo(x: number | string) { +>foo : (x: string | number) => number +>x : string | number + + return typeof x === "string" +>typeof x === "string" ? x.length // string : x++ : number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + ? x.length // string +>x.length : number +>x : string +>length : number + + : x++; // number +>x++ : number +>x : number +} +function foo2(x: number | string) { +>foo2 : (x: string | number) => string | number +>x : string | number + + // x is assigned in the if true branch, the type is not narrowed + return typeof x === "string" +>typeof x === "string" ? (x = 10 && x)// string | number : x : string | number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + ? (x = 10 && x)// string | number +>(x = 10 && x) : string | number +>x = 10 && x : string | number +>x : string | number +>10 && x : string | number +>x : string | number + + : x; // string | number +>x : string | number +} +function foo3(x: number | string) { +>foo3 : (x: string | number) => string | number +>x : string | number + + // x is assigned in the if false branch, the type is not narrowed + // even though assigned using same type as narrowed expression + return typeof x === "string" +>typeof x === "string" ? (x = "Hello" && x) // string | number : x : string | number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + ? (x = "Hello" && x) // string | number +>(x = "Hello" && x) : string | number +>x = "Hello" && x : string | number +>x : string | number +>"Hello" && x : string | number +>x : string | number + + : x; // string | number +>x : string | number +} +function foo4(x: number | string) { +>foo4 : (x: string | number) => string | number +>x : string | number + + // false branch updates the variable - so here it is not number + // even though assigned using same type as narrowed expression + return typeof x === "string" +>typeof x === "string" ? x // string | number : (x = 10 && x) : string | number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + ? x // string | number +>x : string | number + + : (x = 10 && x); // string | number +>(x = 10 && x) : string | number +>x = 10 && x : string | number +>x : string | number +>10 && x : string | number +>x : string | number +} +function foo5(x: number | string) { +>foo5 : (x: string | number) => string | number +>x : string | number + + // false branch updates the variable - so here it is not number + return typeof x === "string" +>typeof x === "string" ? x // string | number : (x = "hello" && x) : string | number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + ? x // string | number +>x : string | number + + : (x = "hello" && x); // string | number +>(x = "hello" && x) : string | number +>x = "hello" && x : string | number +>x : string | number +>"hello" && x : string | number +>x : string | number +} +function foo6(x: number | string) { +>foo6 : (x: string | number) => string | number +>x : string | number + + // Modify in both branches + return typeof x === "string" +>typeof x === "string" ? (x = 10 && x) // string | number : (x = "hello" && x) : string | number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + ? (x = 10 && x) // string | number +>(x = 10 && x) : string | number +>x = 10 && x : string | number +>x : string | number +>10 && x : string | number +>x : string | number + + : (x = "hello" && x); // string | number +>(x = "hello" && x) : string | number +>x = "hello" && x : string | number +>x : string | number +>"hello" && x : string | number +>x : string | number +} +function foo7(x: number | string | boolean) { +>foo7 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + return typeof x === "string" +>typeof x === "string" ? x === "hello" // string : typeof x === "boolean" ? x // boolean : x == 10 : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? x === "hello" // string +>x === "hello" : boolean +>x : string + + : typeof x === "boolean" +>typeof x === "boolean" ? x // boolean : x == 10 : boolean +>typeof x === "boolean" : boolean +>typeof x : string +>x : number | boolean + + ? x // boolean +>x : boolean + + : x == 10; // number +>x == 10 : boolean +>x : number +} +function foo8(x: number | string | boolean) { +>foo8 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + var b: number | boolean; +>b : number | boolean + + return typeof x === "string" +>typeof x === "string" ? x === "hello" : ((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)) : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? x === "hello" +>x === "hello" : boolean +>x : string + + : ((b = x) && // number | boolean +>((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)) : boolean +>(b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10) : boolean +>(b = x) : number | boolean +>b = x : number | boolean +>b : number | boolean +>x : number | boolean + + (typeof x === "boolean" +>(typeof x === "boolean" ? x // boolean : x == 10) : boolean +>typeof x === "boolean" ? x // boolean : x == 10 : boolean +>typeof x === "boolean" : boolean +>typeof x : string +>x : number | boolean + + ? x // boolean +>x : boolean + + : x == 10)); // number +>x == 10 : boolean +>x : number +} +function foo9(x: number | string) { +>foo9 : (x: string | number) => boolean +>x : string | number + + var y = 10; +>y : number + + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + return typeof x === "string" +>typeof x === "string" ? ((y = x.length) && x === "hello") // string : x === 10 : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + ? ((y = x.length) && x === "hello") // string +>((y = x.length) && x === "hello") : boolean +>(y = x.length) && x === "hello" : boolean +>(y = x.length) : number +>y = x.length : number +>y : number +>x.length : number +>x : string +>length : number +>x === "hello" : boolean +>x : string + + : x === 10; // number +>x === 10 : boolean +>x : number +} +function foo10(x: number | string | boolean) { +>foo10 : (x: string | number | boolean) => string +>x : string | number | boolean + + // Mixing typeguards + var b: boolean | number; +>b : number | boolean + + return typeof x === "string" +>typeof x === "string" ? x // string : ((b = x) // x is number | boolean && typeof x === "number" && x.toString()) : string +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? x // string +>x : string + + : ((b = x) // x is number | boolean +>((b = x) // x is number | boolean && typeof x === "number" && x.toString()) : string +>(b = x) // x is number | boolean && typeof x === "number" && x.toString() : string +>(b = x) // x is number | boolean && typeof x === "number" : boolean +>(b = x) : number | boolean +>b = x : number | boolean +>b : number | boolean +>x : number | boolean + + && typeof x === "number" +>typeof x === "number" : boolean +>typeof x : string +>x : number | boolean + + && x.toString()); // x is number +>x.toString() : string +>x.toString : (radix?: number) => string +>x : number +>toString : (radix?: number) => string +} +function foo11(x: number | string | boolean) { +>foo11 : (x: string | number | boolean) => string | number | boolean +>x : string | number | boolean + + // Mixing typeguards + // Assigning value to x deep inside another guard stops narrowing of type too + var b: number | boolean | string; +>b : string | number | boolean + + return typeof x === "string" +>typeof x === "string" ? x // number | boolean | string - changed in the false branch : ((b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x && x) : string | number | boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? x // number | boolean | string - changed in the false branch +>x : string | number | boolean + + : ((b = x) // x is number | boolean | string - because the assignment changed it +>((b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x && x) : string | number | boolean +>(b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x && x : string | number | boolean +>(b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) : number +>(b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" : boolean +>(b = x) : string | number | boolean +>b = x : string | number | boolean +>b : string | number | boolean +>x : string | number | boolean + + && typeof x === "number" +>typeof x === "number" : boolean +>typeof x : string +>x : string | number | boolean + + && (x = 10) // assignment to x +>(x = 10) : number +>x = 10 : number +>x : string | number | boolean + + && x); // x is number | boolean | string +>x : string | number | boolean +} +function foo12(x: number | string | boolean) { +>foo12 : (x: string | number | boolean) => number +>x : string | number | boolean + + // Mixing typeguards + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + var b: number | boolean | string; +>b : string | number | boolean + + return typeof x === "string" +>typeof x === "string" ? (x = 10 && x.toString().length) // number | boolean | string - changed here : ((b = x) // x is number | boolean | string - changed in true branch && typeof x === "number" && x) : number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? (x = 10 && x.toString().length) // number | boolean | string - changed here +>(x = 10 && x.toString().length) : number +>x = 10 && x.toString().length : number +>x : string | number | boolean +>10 && x.toString().length : number +>x.toString().length : number +>x.toString() : string +>x.toString : () => string +>x : string | number | boolean +>toString : () => string +>length : number + + : ((b = x) // x is number | boolean | string - changed in true branch +>((b = x) // x is number | boolean | string - changed in true branch && typeof x === "number" && x) : number +>(b = x) // x is number | boolean | string - changed in true branch && typeof x === "number" && x : number +>(b = x) // x is number | boolean | string - changed in true branch && typeof x === "number" : boolean +>(b = x) : string | number | boolean +>b = x : string | number | boolean +>b : string | number | boolean +>x : string | number | boolean + + && typeof x === "number" +>typeof x === "number" : boolean +>typeof x : string +>x : string | number | boolean + + && x); // x is number +>x : number +} diff --git a/tests/baselines/reference/typeGuardsInExternalModule.js b/tests/baselines/reference/typeGuardsInExternalModule.js new file mode 100644 index 00000000000..29950ece95a --- /dev/null +++ b/tests/baselines/reference/typeGuardsInExternalModule.js @@ -0,0 +1,47 @@ +//// [typeGuardsInExternalModule.ts] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// local variable in external module +var num: number; +var var1: string | number; +if (typeof var1 === "string") { + num = var1.length; // string +} +else { + num = var1; // number +} + +// exported variable in external module +var strOrNum: string | number; +export var var2: string | number; +if (typeof var2 === "string") { + // export makes the var property and not variable + strOrNum = var2; // string | number +} +else { + strOrNum = var2; // number | string +} + +//// [typeGuardsInExternalModule.js] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. +// local variable in external module +var num; +var var1; +if (typeof var1 === "string") { + num = var1.length; // string +} +else { + num = var1; // number +} +// exported variable in external module +var strOrNum; +exports.var2; +if (typeof exports.var2 === "string") { + // export makes the var property and not variable + strOrNum = exports.var2; // string | number +} +else { + strOrNum = exports.var2; // number | string +} diff --git a/tests/baselines/reference/typeGuardsInExternalModule.types b/tests/baselines/reference/typeGuardsInExternalModule.types new file mode 100644 index 00000000000..09038ef1c1a --- /dev/null +++ b/tests/baselines/reference/typeGuardsInExternalModule.types @@ -0,0 +1,54 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInExternalModule.ts === +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// local variable in external module +var num: number; +>num : number + +var var1: string | number; +>var1 : string | number + +if (typeof var1 === "string") { +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number + + num = var1.length; // string +>num = var1.length : number +>num : number +>var1.length : number +>var1 : string +>length : number +} +else { + num = var1; // number +>num = var1 : number +>num : number +>var1 : number +} + +// exported variable in external module +var strOrNum: string | number; +>strOrNum : string | number + +export var var2: string | number; +>var2 : string | number + +if (typeof var2 === "string") { +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number + + // export makes the var property and not variable + strOrNum = var2; // string | number +>strOrNum = var2 : string | number +>strOrNum : string | number +>var2 : string | number +} +else { + strOrNum = var2; // number | string +>strOrNum = var2 : string | number +>strOrNum : string | number +>var2 : string | number +} diff --git a/tests/baselines/reference/typeGuardsInFunction.js b/tests/baselines/reference/typeGuardsInFunction.js new file mode 100644 index 00000000000..53bfb5fa6bb --- /dev/null +++ b/tests/baselines/reference/typeGuardsInFunction.js @@ -0,0 +1,165 @@ +//// [typeGuardsInFunction.ts] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var var1: string | number; +// Inside function declaration +function f(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +} +// local function declaration +function f1(param: string | number) { + var var2: string | number; + function f2(param1: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + + // local + var var3: string | number; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + } +} +// Function expression +function f2(param: string | number) { + // variables in function declaration + var var2: string | number; + // variables in function expressions + var r = function (param1: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + + // local + var var3: string | number; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + } (param); +} +// Arrow expression +function f3(param: string | number) { + // variables in function declaration + var var2: string | number; + // variables in function expressions + var r = ((param1: string | number) => { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + + // local + var var3: string | number; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + })(param); +} +// Return type of function +// Inside function declaration +var strOrNum: string | number; +function f4() { + var var2: string | number = strOrNum; + return var2; +} +strOrNum = typeof f4() === "string" && f4(); // string | number + +//// [typeGuardsInFunction.js] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. +// variables in global +var num; +var var1; +// Inside function declaration +function f(param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + // parameters in function declaration + num = typeof param === "string" && param.length; // string +} +// local function declaration +function f1(param) { + var var2; + function f2(param1) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + // local + var var3; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + } +} +// Function expression +function f2(param) { + // variables in function declaration + var var2; + // variables in function expressions + var r = function (param1) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + // local + var var3; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + }(param); +} +// Arrow expression +function f3(param) { + // variables in function declaration + var var2; + // variables in function expressions + var r = (function (param1) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + // local + var var3; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + })(param); +} +// Return type of function +// Inside function declaration +var strOrNum; +function f4() { + var var2 = strOrNum; + return var2; +} +strOrNum = typeof f4() === "string" && f4(); // string | number diff --git a/tests/baselines/reference/typeGuardsInFunction.types b/tests/baselines/reference/typeGuardsInFunction.types new file mode 100644 index 00000000000..a7b25f486aa --- /dev/null +++ b/tests/baselines/reference/typeGuardsInFunction.types @@ -0,0 +1,319 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInFunction.ts === +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +>num : number + +var var1: string | number; +>var1 : string | number + +// Inside function declaration +function f(param: string | number) { +>f : (param: string | number) => void +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number +} +// local function declaration +function f1(param: string | number) { +>f1 : (param: string | number) => void +>param : string | number + + var var2: string | number; +>var2 : string | number + + function f2(param1: string | number) { +>f2 : (param1: string | number) => void +>param1 : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + + // local + var var3: string | number; +>var3 : string | number + + num = typeof var3 === "string" && var3.length; // string +>num = typeof var3 === "string" && var3.length : number +>num : number +>typeof var3 === "string" && var3.length : number +>typeof var3 === "string" : boolean +>typeof var3 : string +>var3 : string | number +>var3.length : number +>var3 : string +>length : number + + num = typeof param1 === "string" && param1.length; // string +>num = typeof param1 === "string" && param1.length : number +>num : number +>typeof param1 === "string" && param1.length : number +>typeof param1 === "string" : boolean +>typeof param1 : string +>param1 : string | number +>param1.length : number +>param1 : string +>length : number + } +} +// Function expression +function f2(param: string | number) { +>f2 : (param: string | number) => void +>param : string | number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + // variables in function expressions + var r = function (param1: string | number) { +>r : void +>function (param1: string | number) { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables from outer function declaration num = typeof var2 === "string" && var2.length; // string // parameters in outer declaration num = typeof param === "string" && param.length; // string // local var var3: string | number; num = typeof var3 === "string" && var3.length; // string num = typeof param1 === "string" && param1.length; // string } (param) : void +>function (param1: string | number) { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables from outer function declaration num = typeof var2 === "string" && var2.length; // string // parameters in outer declaration num = typeof param === "string" && param.length; // string // local var var3: string | number; num = typeof var3 === "string" && var3.length; // string num = typeof param1 === "string" && param1.length; // string } : (param1: string | number) => void +>param1 : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + + // local + var var3: string | number; +>var3 : string | number + + num = typeof var3 === "string" && var3.length; // string +>num = typeof var3 === "string" && var3.length : number +>num : number +>typeof var3 === "string" && var3.length : number +>typeof var3 === "string" : boolean +>typeof var3 : string +>var3 : string | number +>var3.length : number +>var3 : string +>length : number + + num = typeof param1 === "string" && param1.length; // string +>num = typeof param1 === "string" && param1.length : number +>num : number +>typeof param1 === "string" && param1.length : number +>typeof param1 === "string" : boolean +>typeof param1 : string +>param1 : string | number +>param1.length : number +>param1 : string +>length : number + + } (param); +>param : string | number +} +// Arrow expression +function f3(param: string | number) { +>f3 : (param: string | number) => void +>param : string | number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + // variables in function expressions + var r = ((param1: string | number) => { +>r : void +>((param1: string | number) => { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables from outer function declaration num = typeof var2 === "string" && var2.length; // string // parameters in outer declaration num = typeof param === "string" && param.length; // string // local var var3: string | number; num = typeof var3 === "string" && var3.length; // string num = typeof param1 === "string" && param1.length; // string })(param) : void +>((param1: string | number) => { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables from outer function declaration num = typeof var2 === "string" && var2.length; // string // parameters in outer declaration num = typeof param === "string" && param.length; // string // local var var3: string | number; num = typeof var3 === "string" && var3.length; // string num = typeof param1 === "string" && param1.length; // string }) : (param1: string | number) => void +>(param1: string | number) => { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables from outer function declaration num = typeof var2 === "string" && var2.length; // string // parameters in outer declaration num = typeof param === "string" && param.length; // string // local var var3: string | number; num = typeof var3 === "string" && var3.length; // string num = typeof param1 === "string" && param1.length; // string } : (param1: string | number) => void +>param1 : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + + // local + var var3: string | number; +>var3 : string | number + + num = typeof var3 === "string" && var3.length; // string +>num = typeof var3 === "string" && var3.length : number +>num : number +>typeof var3 === "string" && var3.length : number +>typeof var3 === "string" : boolean +>typeof var3 : string +>var3 : string | number +>var3.length : number +>var3 : string +>length : number + + num = typeof param1 === "string" && param1.length; // string +>num = typeof param1 === "string" && param1.length : number +>num : number +>typeof param1 === "string" && param1.length : number +>typeof param1 === "string" : boolean +>typeof param1 : string +>param1 : string | number +>param1.length : number +>param1 : string +>length : number + + })(param); +>param : string | number +} +// Return type of function +// Inside function declaration +var strOrNum: string | number; +>strOrNum : string | number + +function f4() { +>f4 : () => string | number + + var var2: string | number = strOrNum; +>var2 : string | number +>strOrNum : string | number + + return var2; +>var2 : string | number +} +strOrNum = typeof f4() === "string" && f4(); // string | number +>strOrNum = typeof f4() === "string" && f4() : string | number +>strOrNum : string | number +>typeof f4() === "string" && f4() : string | number +>typeof f4() === "string" : boolean +>typeof f4() : string +>f4() : string | number +>f4 : () => string | number +>f4() : string | number +>f4 : () => string | number + diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js new file mode 100644 index 00000000000..1152cd8d8b5 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js @@ -0,0 +1,131 @@ +//// [typeGuardsInFunctionAndModuleBlock.ts] +// typeguards are scoped in function/module block + +function foo(x: number | string | boolean) { + return typeof x === "string" + ? x + : function f() { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number | string + } (); +} +function foo2(x: number | string | boolean) { + return typeof x === "string" + ? x + : function f(a: number | boolean) { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number | string + } (x); // x here is narrowed to number | boolean +} +function foo3(x: number | string | boolean) { + return typeof x === "string" + ? x + : (() => { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number | string + })(); +} +function foo4(x: number | string | boolean) { + return typeof x === "string" + ? x + : ((a: number | boolean) => { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number | string + })(x); // x here is narrowed to number | boolean +} +module m { + var x: number | string | boolean; + module m2 { + var b = x; // new scope - number | boolean | string + var y: string; + if (typeof x === "string") { + y = x // string; + } else { + y = typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number + } + } +} +module m1 { + var x: number | string | boolean; + module m2.m3 { + var b = x; // new scope - number | boolean | string + var y: string; + if (typeof x === "string") { + y = x // string; + } else { + y = typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number + } + } +} + +//// [typeGuardsInFunctionAndModuleBlock.js] +// typeguards are scoped in function/module block +function foo(x) { + return typeof x === "string" ? x : function f() { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" ? x.toString() : x.toString(); // number | string + }(); +} +function foo2(x) { + return typeof x === "string" ? x : function f(a) { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" ? x.toString() : x.toString(); // number | string + }(x); // x here is narrowed to number | boolean +} +function foo3(x) { + return typeof x === "string" ? x : (function () { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" ? x.toString() : x.toString(); // number | string + })(); +} +function foo4(x) { + return typeof x === "string" ? x : (function (a) { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" ? x.toString() : x.toString(); // number | string + })(x); // x here is narrowed to number | boolean +} +var m; +(function (m) { + var x; + var m2; + (function (m2) { + var b = x; // new scope - number | boolean | string + var y; + if (typeof x === "string") { + y = x; // string; + } + else { + y = typeof x === "boolean" ? x.toString() : x.toString(); // number + } + })(m2 || (m2 = {})); +})(m || (m = {})); +var m1; +(function (m1) { + var x; + var m2; + (function (m2) { + var m3; + (function (m3) { + var b = x; // new scope - number | boolean | string + var y; + if (typeof x === "string") { + y = x; // string; + } + else { + y = typeof x === "boolean" ? x.toString() : x.toString(); // number + } + })(m3 = m2.m3 || (m2.m3 = {})); + })(m2 || (m2 = {})); +})(m1 || (m1 = {})); diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types new file mode 100644 index 00000000000..c1ba1a7d639 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types @@ -0,0 +1,274 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts === +// typeguards are scoped in function/module block + +function foo(x: number | string | boolean) { +>foo : (x: string | number | boolean) => string +>x : string | number | boolean + + return typeof x === "string" +>typeof x === "string" ? x : function f() { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string } () : string +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? x +>x : string + + : function f() { +>function f() { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string } () : string +>function f() { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string } : () => string +>f : () => string + + var b = x; // new scope - number | boolean | string +>b : string | number | boolean +>x : string | number | boolean + + return typeof x === "boolean" +>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string +>typeof x === "boolean" : boolean +>typeof x : string +>x : string | number | boolean + + ? x.toString() // boolean +>x.toString() : string +>x.toString : () => string +>x : boolean +>toString : () => string + + : x.toString(); // number | string +>x.toString() : string +>x.toString : () => string +>x : string | number +>toString : () => string + + } (); +} +function foo2(x: number | string | boolean) { +>foo2 : (x: string | number | boolean) => string +>x : string | number | boolean + + return typeof x === "string" +>typeof x === "string" ? x : function f(a: number | boolean) { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string } (x) : string +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? x +>x : string + + : function f(a: number | boolean) { +>function f(a: number | boolean) { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string } (x) : string +>function f(a: number | boolean) { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string } : (a: number | boolean) => string +>f : (a: number | boolean) => string +>a : number | boolean + + var b = x; // new scope - number | boolean | string +>b : string | number | boolean +>x : string | number | boolean + + return typeof x === "boolean" +>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string +>typeof x === "boolean" : boolean +>typeof x : string +>x : string | number | boolean + + ? x.toString() // boolean +>x.toString() : string +>x.toString : () => string +>x : boolean +>toString : () => string + + : x.toString(); // number | string +>x.toString() : string +>x.toString : () => string +>x : string | number +>toString : () => string + + } (x); // x here is narrowed to number | boolean +>x : number | boolean +} +function foo3(x: number | string | boolean) { +>foo3 : (x: string | number | boolean) => string +>x : string | number | boolean + + return typeof x === "string" +>typeof x === "string" ? x : (() => { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string })() : string +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? x +>x : string + + : (() => { +>(() => { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string })() : string +>(() => { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string }) : () => string +>() => { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string } : () => string + + var b = x; // new scope - number | boolean | string +>b : string | number | boolean +>x : string | number | boolean + + return typeof x === "boolean" +>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string +>typeof x === "boolean" : boolean +>typeof x : string +>x : string | number | boolean + + ? x.toString() // boolean +>x.toString() : string +>x.toString : () => string +>x : boolean +>toString : () => string + + : x.toString(); // number | string +>x.toString() : string +>x.toString : () => string +>x : string | number +>toString : () => string + + })(); +} +function foo4(x: number | string | boolean) { +>foo4 : (x: string | number | boolean) => string +>x : string | number | boolean + + return typeof x === "string" +>typeof x === "string" ? x : ((a: number | boolean) => { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string })(x) : string +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + ? x +>x : string + + : ((a: number | boolean) => { +>((a: number | boolean) => { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string })(x) : string +>((a: number | boolean) => { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string }) : (a: number | boolean) => string +>(a: number | boolean) => { var b = x; // new scope - number | boolean | string return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number | string } : (a: number | boolean) => string +>a : number | boolean + + var b = x; // new scope - number | boolean | string +>b : string | number | boolean +>x : string | number | boolean + + return typeof x === "boolean" +>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string +>typeof x === "boolean" : boolean +>typeof x : string +>x : string | number | boolean + + ? x.toString() // boolean +>x.toString() : string +>x.toString : () => string +>x : boolean +>toString : () => string + + : x.toString(); // number | string +>x.toString() : string +>x.toString : () => string +>x : string | number +>toString : () => string + + })(x); // x here is narrowed to number | boolean +>x : number | boolean +} +module m { +>m : typeof m + + var x: number | string | boolean; +>x : string | number | boolean + + module m2 { +>m2 : typeof m2 + + var b = x; // new scope - number | boolean | string +>b : string | number | boolean +>x : string | number | boolean + + var y: string; +>y : string + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + y = x // string; +>y = x : string +>y : string +>x : string + + } else { + y = typeof x === "boolean" +>y = typeof x === "boolean" ? x.toString() // boolean : x.toString() : string +>y : string +>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string +>typeof x === "boolean" : boolean +>typeof x : string +>x : number | boolean + + ? x.toString() // boolean +>x.toString() : string +>x.toString : () => string +>x : boolean +>toString : () => string + + : x.toString(); // number +>x.toString() : string +>x.toString : (radix?: number) => string +>x : number +>toString : (radix?: number) => string + } + } +} +module m1 { +>m1 : typeof m1 + + var x: number | string | boolean; +>x : string | number | boolean + + module m2.m3 { +>m2 : typeof m2 +>m3 : typeof m3 + + var b = x; // new scope - number | boolean | string +>b : string | number | boolean +>x : string | number | boolean + + var y: string; +>y : string + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + y = x // string; +>y = x : string +>y : string +>x : string + + } else { + y = typeof x === "boolean" +>y = typeof x === "boolean" ? x.toString() // boolean : x.toString() : string +>y : string +>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string +>typeof x === "boolean" : boolean +>typeof x : string +>x : number | boolean + + ? x.toString() // boolean +>x.toString() : string +>x.toString : () => string +>x : boolean +>toString : () => string + + : x.toString(); // number +>x.toString() : string +>x.toString : (radix?: number) => string +>x : number +>toString : (radix?: number) => string + } + } +} diff --git a/tests/baselines/reference/typeGuardsInGlobal.js b/tests/baselines/reference/typeGuardsInGlobal.js new file mode 100644 index 00000000000..6e1b1c3dd82 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInGlobal.js @@ -0,0 +1,27 @@ +//// [typeGuardsInGlobal.ts] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var var1: string | number; +if (typeof var1 === "string") { + num = var1.length; // string +} +else { + num = var1; // number +} + + +//// [typeGuardsInGlobal.js] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. +// variables in global +var num; +var var1; +if (typeof var1 === "string") { + num = var1.length; // string +} +else { + num = var1; // number +} diff --git a/tests/baselines/reference/typeGuardsInGlobal.types b/tests/baselines/reference/typeGuardsInGlobal.types new file mode 100644 index 00000000000..08b681dbb05 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInGlobal.types @@ -0,0 +1,30 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInGlobal.ts === +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +>num : number + +var var1: string | number; +>var1 : string | number + +if (typeof var1 === "string") { +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number + + num = var1.length; // string +>num = var1.length : number +>num : number +>var1.length : number +>var1 : string +>length : number +} +else { + num = var1; // number +>num = var1 : number +>num : number +>var1 : number +} + diff --git a/tests/baselines/reference/typeGuardsInIfStatement.js b/tests/baselines/reference/typeGuardsInIfStatement.js new file mode 100644 index 00000000000..6b1fb4b062c --- /dev/null +++ b/tests/baselines/reference/typeGuardsInIfStatement.js @@ -0,0 +1,287 @@ +//// [typeGuardsInIfStatement.ts] +// In the true branch statement of an �if� statement, +// the type of a variable or parameter is narrowed by any type guard in the �if� condition when true, +// provided the true branch statement contains no assignments to the variable or parameter. +// In the false branch statement of an �if� statement, +// the type of a variable or parameter is narrowed by any type guard in the �if� condition when false, +// provided the false branch statement contains no assignments to the variable or parameter +function foo(x: number | string) { + if (typeof x === "string") { + return x.length; // string + } + else { + return x++; // number + } +} +function foo2(x: number | string) { + // x is assigned in the if true branch, the type is not narrowed + if (typeof x === "string") { + x = 10; + return x; // string | number + } + else { + return x; // string | number + } +} +function foo3(x: number | string) { + // x is assigned in the if true branch, the type is not narrowed + if (typeof x === "string") { + x = "Hello"; // even though assigned using same type as narrowed expression + return x; // string | number + } + else { + return x; // string | number + } +} +function foo4(x: number | string) { + // false branch updates the variable - so here it is not number + if (typeof x === "string") { + return x; // string | number + } + else { + x = 10; // even though assigned number - this should result in x to be string | number + return x; // string | number + } +} +function foo5(x: number | string) { + // false branch updates the variable - so here it is not number + if (typeof x === "string") { + return x; // string | number + } + else { + x = "hello"; + return x; // string | number + } +} +function foo6(x: number | string) { + // Modify in both branches + if (typeof x === "string") { + x = 10; + return x; // string | number + } + else { + x = "hello"; + return x; // string | number + } +} +function foo7(x: number | string | boolean) { + if (typeof x === "string") { + return x === "hello"; // string + } + else if (typeof x === "boolean") { + return x; // boolean + } + else { + return x == 10; // number + } +} +function foo8(x: number | string | boolean) { + if (typeof x === "string") { + return x === "hello"; // string + } + else { + var b: number | boolean = x; // number | boolean + if (typeof x === "boolean") { + return x; // boolean + } + else { + return x == 10; // number + } + } +} +function foo9(x: number | string) { + var y = 10; + if (typeof x === "string") { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + y = x.length; + return x === "hello"; // string + } + else { + return x == 10; // number + } +} +function foo10(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + if (typeof x === "string") { + return x === "hello"; // string + } + else { + var y: boolean | string; + var b = x; // number | boolean + return typeof x === "number" + ? x === 10 // number + : x; // x should be boolean + } +} +function foo11(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x deep inside another guard stops narrowing of type too + if (typeof x === "string") { + return x; // string | number | boolean - x changed in else branch + } + else { + var y: number| boolean | string; + var b = x; // number | boolean | string - because below we are changing value of x in if statement + return typeof x === "number" + ? ( + // change value of x + x = 10 && x.toString() // number | boolean | string + ) + : ( + // do not change value + y = x && x.toString() // number | boolean | string + ); + } +} +function foo12(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + if (typeof x === "string") { + return x.toString(); // string | number | boolean - x changed in else branch + } + else { + x = 10; + var b = x; // number | boolean | string + return typeof x === "number" + ? x.toString() // number + : x.toString(); // boolean | string + } +} + +//// [typeGuardsInIfStatement.js] +// In the true branch statement of an �if� statement, +// the type of a variable or parameter is narrowed by any type guard in the �if� condition when true, +// provided the true branch statement contains no assignments to the variable or parameter. +// In the false branch statement of an �if� statement, +// the type of a variable or parameter is narrowed by any type guard in the �if� condition when false, +// provided the false branch statement contains no assignments to the variable or parameter +function foo(x) { + if (typeof x === "string") { + return x.length; // string + } + else { + return x++; // number + } +} +function foo2(x) { + // x is assigned in the if true branch, the type is not narrowed + if (typeof x === "string") { + x = 10; + return x; // string | number + } + else { + return x; // string | number + } +} +function foo3(x) { + // x is assigned in the if true branch, the type is not narrowed + if (typeof x === "string") { + x = "Hello"; // even though assigned using same type as narrowed expression + return x; // string | number + } + else { + return x; // string | number + } +} +function foo4(x) { + // false branch updates the variable - so here it is not number + if (typeof x === "string") { + return x; // string | number + } + else { + x = 10; // even though assigned number - this should result in x to be string | number + return x; // string | number + } +} +function foo5(x) { + // false branch updates the variable - so here it is not number + if (typeof x === "string") { + return x; // string | number + } + else { + x = "hello"; + return x; // string | number + } +} +function foo6(x) { + // Modify in both branches + if (typeof x === "string") { + x = 10; + return x; // string | number + } + else { + x = "hello"; + return x; // string | number + } +} +function foo7(x) { + if (typeof x === "string") { + return x === "hello"; // string + } + else if (typeof x === "boolean") { + return x; // boolean + } + else { + return x == 10; // number + } +} +function foo8(x) { + if (typeof x === "string") { + return x === "hello"; // string + } + else { + var b = x; // number | boolean + if (typeof x === "boolean") { + return x; // boolean + } + else { + return x == 10; // number + } + } +} +function foo9(x) { + var y = 10; + if (typeof x === "string") { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + y = x.length; + return x === "hello"; // string + } + else { + return x == 10; // number + } +} +function foo10(x) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + if (typeof x === "string") { + return x === "hello"; // string + } + else { + var y; + var b = x; // number | boolean + return typeof x === "number" ? x === 10 : x; // x should be boolean + } +} +function foo11(x) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x deep inside another guard stops narrowing of type too + if (typeof x === "string") { + return x; // string | number | boolean - x changed in else branch + } + else { + var y; + var b = x; // number | boolean | string - because below we are changing value of x in if statement + return typeof x === "number" ? (x = 10 && x.toString()) : (y = x && x.toString()); + } +} +function foo12(x) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + if (typeof x === "string") { + return x.toString(); // string | number | boolean - x changed in else branch + } + else { + x = 10; + var b = x; // number | boolean | string + return typeof x === "number" ? x.toString() : x.toString(); // boolean | string + } +} diff --git a/tests/baselines/reference/typeGuardsInIfStatement.types b/tests/baselines/reference/typeGuardsInIfStatement.types new file mode 100644 index 00000000000..f443c0d7e0d --- /dev/null +++ b/tests/baselines/reference/typeGuardsInIfStatement.types @@ -0,0 +1,371 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts === +// In the true branch statement of an �if� statement, +// the type of a variable or parameter is narrowed by any type guard in the �if� condition when true, +// provided the true branch statement contains no assignments to the variable or parameter. +// In the false branch statement of an �if� statement, +// the type of a variable or parameter is narrowed by any type guard in the �if� condition when false, +// provided the false branch statement contains no assignments to the variable or parameter +function foo(x: number | string) { +>foo : (x: string | number) => number +>x : string | number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + return x.length; // string +>x.length : number +>x : string +>length : number + } + else { + return x++; // number +>x++ : number +>x : number + } +} +function foo2(x: number | string) { +>foo2 : (x: string | number) => string | number +>x : string | number + + // x is assigned in the if true branch, the type is not narrowed + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + x = 10; +>x = 10 : number +>x : string | number + + return x; // string | number +>x : string | number + } + else { + return x; // string | number +>x : string | number + } +} +function foo3(x: number | string) { +>foo3 : (x: string | number) => string | number +>x : string | number + + // x is assigned in the if true branch, the type is not narrowed + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + x = "Hello"; // even though assigned using same type as narrowed expression +>x = "Hello" : string +>x : string | number + + return x; // string | number +>x : string | number + } + else { + return x; // string | number +>x : string | number + } +} +function foo4(x: number | string) { +>foo4 : (x: string | number) => string | number +>x : string | number + + // false branch updates the variable - so here it is not number + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + return x; // string | number +>x : string | number + } + else { + x = 10; // even though assigned number - this should result in x to be string | number +>x = 10 : number +>x : string | number + + return x; // string | number +>x : string | number + } +} +function foo5(x: number | string) { +>foo5 : (x: string | number) => string | number +>x : string | number + + // false branch updates the variable - so here it is not number + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + return x; // string | number +>x : string | number + } + else { + x = "hello"; +>x = "hello" : string +>x : string | number + + return x; // string | number +>x : string | number + } +} +function foo6(x: number | string) { +>foo6 : (x: string | number) => string | number +>x : string | number + + // Modify in both branches + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + x = 10; +>x = 10 : number +>x : string | number + + return x; // string | number +>x : string | number + } + else { + x = "hello"; +>x = "hello" : string +>x : string | number + + return x; // string | number +>x : string | number + } +} +function foo7(x: number | string | boolean) { +>foo7 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + return x === "hello"; // string +>x === "hello" : boolean +>x : string + } + else if (typeof x === "boolean") { +>typeof x === "boolean" : boolean +>typeof x : string +>x : number | boolean + + return x; // boolean +>x : boolean + } + else { + return x == 10; // number +>x == 10 : boolean +>x : number + } +} +function foo8(x: number | string | boolean) { +>foo8 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + return x === "hello"; // string +>x === "hello" : boolean +>x : string + } + else { + var b: number | boolean = x; // number | boolean +>b : number | boolean +>x : number | boolean + + if (typeof x === "boolean") { +>typeof x === "boolean" : boolean +>typeof x : string +>x : number | boolean + + return x; // boolean +>x : boolean + } + else { + return x == 10; // number +>x == 10 : boolean +>x : number + } + } +} +function foo9(x: number | string) { +>foo9 : (x: string | number) => boolean +>x : string | number + + var y = 10; +>y : number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + y = x.length; +>y = x.length : number +>y : number +>x.length : number +>x : string +>length : number + + return x === "hello"; // string +>x === "hello" : boolean +>x : string + } + else { + return x == 10; // number +>x == 10 : boolean +>x : number + } +} +function foo10(x: number | string | boolean) { +>foo10 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + // Mixing typeguard narrowing in if statement with conditional expression typeguard + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + return x === "hello"; // string +>x === "hello" : boolean +>x : string + } + else { + var y: boolean | string; +>y : string | boolean + + var b = x; // number | boolean +>b : number | boolean +>x : number | boolean + + return typeof x === "number" +>typeof x === "number" ? x === 10 // number : x : boolean +>typeof x === "number" : boolean +>typeof x : string +>x : number | boolean + + ? x === 10 // number +>x === 10 : boolean +>x : number + + : x; // x should be boolean +>x : boolean + } +} +function foo11(x: number | string | boolean) { +>foo11 : (x: string | number | boolean) => string | number | boolean +>x : string | number | boolean + + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x deep inside another guard stops narrowing of type too + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + return x; // string | number | boolean - x changed in else branch +>x : string | number | boolean + } + else { + var y: number| boolean | string; +>y : string | number | boolean + + var b = x; // number | boolean | string - because below we are changing value of x in if statement +>b : string | number | boolean +>x : string | number | boolean + + return typeof x === "number" +>typeof x === "number" ? ( // change value of x x = 10 && x.toString() // number | boolean | string ) : ( // do not change value y = x && x.toString() // number | boolean | string ) : string +>typeof x === "number" : boolean +>typeof x : string +>x : string | number | boolean + + ? ( +>( // change value of x x = 10 && x.toString() // number | boolean | string ) : string + + // change value of x + x = 10 && x.toString() // number | boolean | string +>x = 10 && x.toString() : string +>x : string | number | boolean +>10 && x.toString() : string +>x.toString() : string +>x.toString : () => string +>x : string | number | boolean +>toString : () => string + + ) + : ( +>( // do not change value y = x && x.toString() // number | boolean | string ) : string + + // do not change value + y = x && x.toString() // number | boolean | string +>y = x && x.toString() : string +>y : string | number | boolean +>x && x.toString() : string +>x : string | number | boolean +>x.toString() : string +>x.toString : () => string +>x : string | number | boolean +>toString : () => string + + ); + } +} +function foo12(x: number | string | boolean) { +>foo12 : (x: string | number | boolean) => string +>x : string | number | boolean + + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + return x.toString(); // string | number | boolean - x changed in else branch +>x.toString() : string +>x.toString : () => string +>x : string | number | boolean +>toString : () => string + } + else { + x = 10; +>x = 10 : number +>x : string | number | boolean + + var b = x; // number | boolean | string +>b : string | number | boolean +>x : string | number | boolean + + return typeof x === "number" +>typeof x === "number" ? x.toString() // number : x.toString() : string +>typeof x === "number" : boolean +>typeof x : string +>x : string | number | boolean + + ? x.toString() // number +>x.toString() : string +>x.toString : (radix?: number) => string +>x : number +>toString : (radix?: number) => string + + : x.toString(); // boolean | string +>x.toString() : string +>x.toString : () => string +>x : string | boolean +>toString : () => string + } +} diff --git a/tests/baselines/reference/typeGuardsInModule.js b/tests/baselines/reference/typeGuardsInModule.js new file mode 100644 index 00000000000..c72b9e58aa2 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInModule.js @@ -0,0 +1,174 @@ +//// [typeGuardsInModule.ts] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var strOrNum: string | number; +var var1: string | number; +// Inside module +module m1 { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in module declaration + var var2: string | number; + if (typeof var2 === "string") { + num = var2.length; // string + } + else { + num = var2; // number + } + + // exported variable in the module + export var var3: string | number; + if (typeof var3 === "string") { + strOrNum = var3; // string | number + } + else { + strOrNum = var3; // string | number + } +} +// local module +module m2 { + var var2: string | number; + export var var3: string | number; + module m3 { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // local variables from outer module declaration + num = typeof var2 === "string" && var2.length; // string + + // exported variable from outer the module + strOrNum = typeof var3 === "string" && var3; // string | number + + // variables in module declaration + var var4: string | number; + if (typeof var4 === "string") { + num = var4.length; // string + } + else { + num = var4; // number + } + + // exported variable in the module + export var var5: string | number; + if (typeof var5 === "string") { + strOrNum = var5; // string | number + } + else { + strOrNum = var5; // string | number + } + } +} +// Dotted module +module m3.m4 { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in module declaration + var var2: string | number; + if (typeof var2 === "string") { + num = var2.length; // string + } + else { + num = var2; // number + } + + // exported variable in the module + export var var3: string | number; + if (typeof var3 === "string") { + strOrNum = var3; // string | number + } + else { + strOrNum = var3; // string | number + } +} + + +//// [typeGuardsInModule.js] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. +// variables in global +var num; +var strOrNum; +var var1; +// Inside module +var m1; +(function (m1) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in module declaration + var var2; + if (typeof var2 === "string") { + num = var2.length; // string + } + else { + num = var2; // number + } + // exported variable in the module + m1.var3; + if (typeof m1.var3 === "string") { + strOrNum = m1.var3; // string | number + } + else { + strOrNum = m1.var3; // string | number + } +})(m1 || (m1 = {})); +// local module +var m2; +(function (m2) { + var var2; + m2.var3; + var m3; + (function (m3) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // local variables from outer module declaration + num = typeof var2 === "string" && var2.length; // string + // exported variable from outer the module + strOrNum = typeof m2.var3 === "string" && m2.var3; // string | number + // variables in module declaration + var var4; + if (typeof var4 === "string") { + num = var4.length; // string + } + else { + num = var4; // number + } + // exported variable in the module + m3.var5; + if (typeof m3.var5 === "string") { + strOrNum = m3.var5; // string | number + } + else { + strOrNum = m3.var5; // string | number + } + })(m3 || (m3 = {})); +})(m2 || (m2 = {})); +// Dotted module +var m3; +(function (m3) { + var m4; + (function (m4) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in module declaration + var var2; + if (typeof var2 === "string") { + num = var2.length; // string + } + else { + num = var2; // number + } + // exported variable in the module + m4.var3; + if (typeof m4.var3 === "string") { + strOrNum = m4.var3; // string | number + } + else { + strOrNum = m4.var3; // string | number + } + })(m4 = m3.m4 || (m3.m4 = {})); +})(m3 || (m3 = {})); diff --git a/tests/baselines/reference/typeGuardsInModule.types b/tests/baselines/reference/typeGuardsInModule.types new file mode 100644 index 00000000000..82a0345b170 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInModule.types @@ -0,0 +1,228 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInModule.ts === +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var var1: string | number; +>var1 : string | number + +// Inside module +module m1 { +>m1 : typeof m1 + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in module declaration + var var2: string | number; +>var2 : string | number + + if (typeof var2 === "string") { +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number + + num = var2.length; // string +>num = var2.length : number +>num : number +>var2.length : number +>var2 : string +>length : number + } + else { + num = var2; // number +>num = var2 : number +>num : number +>var2 : number + } + + // exported variable in the module + export var var3: string | number; +>var3 : string | number + + if (typeof var3 === "string") { +>typeof var3 === "string" : boolean +>typeof var3 : string +>var3 : string | number + + strOrNum = var3; // string | number +>strOrNum = var3 : string | number +>strOrNum : string | number +>var3 : string | number + } + else { + strOrNum = var3; // string | number +>strOrNum = var3 : string | number +>strOrNum : string | number +>var3 : string | number + } +} +// local module +module m2 { +>m2 : typeof m2 + + var var2: string | number; +>var2 : string | number + + export var var3: string | number; +>var3 : string | number + + module m3 { +>m3 : typeof m3 + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // local variables from outer module declaration + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // exported variable from outer the module + strOrNum = typeof var3 === "string" && var3; // string | number +>strOrNum = typeof var3 === "string" && var3 : string | number +>strOrNum : string | number +>typeof var3 === "string" && var3 : string | number +>typeof var3 === "string" : boolean +>typeof var3 : string +>var3 : string | number +>var3 : string | number + + // variables in module declaration + var var4: string | number; +>var4 : string | number + + if (typeof var4 === "string") { +>typeof var4 === "string" : boolean +>typeof var4 : string +>var4 : string | number + + num = var4.length; // string +>num = var4.length : number +>num : number +>var4.length : number +>var4 : string +>length : number + } + else { + num = var4; // number +>num = var4 : number +>num : number +>var4 : number + } + + // exported variable in the module + export var var5: string | number; +>var5 : string | number + + if (typeof var5 === "string") { +>typeof var5 === "string" : boolean +>typeof var5 : string +>var5 : string | number + + strOrNum = var5; // string | number +>strOrNum = var5 : string | number +>strOrNum : string | number +>var5 : string | number + } + else { + strOrNum = var5; // string | number +>strOrNum = var5 : string | number +>strOrNum : string | number +>var5 : string | number + } + } +} +// Dotted module +module m3.m4 { +>m3 : typeof m3 +>m4 : typeof m4 + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in module declaration + var var2: string | number; +>var2 : string | number + + if (typeof var2 === "string") { +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number + + num = var2.length; // string +>num = var2.length : number +>num : number +>var2.length : number +>var2 : string +>length : number + } + else { + num = var2; // number +>num = var2 : number +>num : number +>var2 : number + } + + // exported variable in the module + export var var3: string | number; +>var3 : string | number + + if (typeof var3 === "string") { +>typeof var3 === "string" : boolean +>typeof var3 : string +>var3 : string | number + + strOrNum = var3; // string | number +>strOrNum = var3 : string | number +>strOrNum : string | number +>var3 : string | number + } + else { + strOrNum = var3; // string | number +>strOrNum = var3 : string | number +>strOrNum : string | number +>var3 : string | number + } +} + diff --git a/tests/baselines/reference/typeGuardsInProperties.js b/tests/baselines/reference/typeGuardsInProperties.js new file mode 100644 index 00000000000..78ad29a28b0 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInProperties.js @@ -0,0 +1,56 @@ +//// [typeGuardsInProperties.ts] + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +var num: number; +var strOrNum: string | number; +class C1 { + private pp1: string | number; + pp2: string | number; + // Inside public accessor getter + get pp3() { + return strOrNum; + } + method() { + strOrNum = typeof this.pp1 === "string" && this.pp1; // string | number + strOrNum = typeof this.pp2 === "string" && this.pp2; // string | number + strOrNum = typeof this.pp3 === "string" && this.pp3; // string | number + } +} +var c1: C1; +strOrNum = typeof c1.pp2 === "string" && c1.pp2; // string | number +strOrNum = typeof c1.pp3 === "string" && c1.pp3; // string | number +var obj1: { + x: string | number; +}; +strOrNum = typeof obj1.x === "string" && obj1.x; // string | number + +//// [typeGuardsInProperties.js] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. +var num; +var strOrNum; +var C1 = (function () { + function C1() { + } + Object.defineProperty(C1.prototype, "pp3", { + // Inside public accessor getter + get: function () { + return strOrNum; + }, + enumerable: true, + configurable: true + }); + C1.prototype.method = function () { + strOrNum = typeof this.pp1 === "string" && this.pp1; // string | number + strOrNum = typeof this.pp2 === "string" && this.pp2; // string | number + strOrNum = typeof this.pp3 === "string" && this.pp3; // string | number + }; + return C1; +})(); +var c1; +strOrNum = typeof c1.pp2 === "string" && c1.pp2; // string | number +strOrNum = typeof c1.pp3 === "string" && c1.pp3; // string | number +var obj1; +strOrNum = typeof obj1.x === "string" && obj1.x; // string | number diff --git a/tests/baselines/reference/typeGuardsInProperties.types b/tests/baselines/reference/typeGuardsInProperties.types new file mode 100644 index 00000000000..46e9ec0d42c --- /dev/null +++ b/tests/baselines/reference/typeGuardsInProperties.types @@ -0,0 +1,120 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInProperties.ts === + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +class C1 { +>C1 : C1 + + private pp1: string | number; +>pp1 : string | number + + pp2: string | number; +>pp2 : string | number + + // Inside public accessor getter + get pp3() { +>pp3 : string | number + + return strOrNum; +>strOrNum : string | number + } + method() { +>method : () => void + + strOrNum = typeof this.pp1 === "string" && this.pp1; // string | number +>strOrNum = typeof this.pp1 === "string" && this.pp1 : string | number +>strOrNum : string | number +>typeof this.pp1 === "string" && this.pp1 : string | number +>typeof this.pp1 === "string" : boolean +>typeof this.pp1 : string +>this.pp1 : string | number +>this : C1 +>pp1 : string | number +>this.pp1 : string | number +>this : C1 +>pp1 : string | number + + strOrNum = typeof this.pp2 === "string" && this.pp2; // string | number +>strOrNum = typeof this.pp2 === "string" && this.pp2 : string | number +>strOrNum : string | number +>typeof this.pp2 === "string" && this.pp2 : string | number +>typeof this.pp2 === "string" : boolean +>typeof this.pp2 : string +>this.pp2 : string | number +>this : C1 +>pp2 : string | number +>this.pp2 : string | number +>this : C1 +>pp2 : string | number + + strOrNum = typeof this.pp3 === "string" && this.pp3; // string | number +>strOrNum = typeof this.pp3 === "string" && this.pp3 : string | number +>strOrNum : string | number +>typeof this.pp3 === "string" && this.pp3 : string | number +>typeof this.pp3 === "string" : boolean +>typeof this.pp3 : string +>this.pp3 : string | number +>this : C1 +>pp3 : string | number +>this.pp3 : string | number +>this : C1 +>pp3 : string | number + } +} +var c1: C1; +>c1 : C1 +>C1 : C1 + +strOrNum = typeof c1.pp2 === "string" && c1.pp2; // string | number +>strOrNum = typeof c1.pp2 === "string" && c1.pp2 : string | number +>strOrNum : string | number +>typeof c1.pp2 === "string" && c1.pp2 : string | number +>typeof c1.pp2 === "string" : boolean +>typeof c1.pp2 : string +>c1.pp2 : string | number +>c1 : C1 +>pp2 : string | number +>c1.pp2 : string | number +>c1 : C1 +>pp2 : string | number + +strOrNum = typeof c1.pp3 === "string" && c1.pp3; // string | number +>strOrNum = typeof c1.pp3 === "string" && c1.pp3 : string | number +>strOrNum : string | number +>typeof c1.pp3 === "string" && c1.pp3 : string | number +>typeof c1.pp3 === "string" : boolean +>typeof c1.pp3 : string +>c1.pp3 : string | number +>c1 : C1 +>pp3 : string | number +>c1.pp3 : string | number +>c1 : C1 +>pp3 : string | number + +var obj1: { +>obj1 : { x: string | number; } + + x: string | number; +>x : string | number + +}; +strOrNum = typeof obj1.x === "string" && obj1.x; // string | number +>strOrNum = typeof obj1.x === "string" && obj1.x : string | number +>strOrNum : string | number +>typeof obj1.x === "string" && obj1.x : string | number +>typeof obj1.x === "string" : boolean +>typeof obj1.x : string +>obj1.x : string | number +>obj1 : { x: string | number; } +>x : string | number +>obj1.x : string | number +>obj1 : { x: string | number; } +>x : string | number + diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js new file mode 100644 index 00000000000..501c5d8a9f9 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js @@ -0,0 +1,96 @@ +//// [typeGuardsInRightOperandOfAndAndOperator.ts] +// In the right operand of a && operation, +// the type of a variable or parameter is narrowed by any type guard in the left operand when true, +// provided the right operand contains no assignments to the variable or parameter. +function foo(x: number | string) { + return typeof x === "string" && x.length === 10; // string +} +function foo2(x: number | string) { + // modify x in right hand operand + return typeof x === "string" && ((x = 10) && x); // string | number +} +function foo3(x: number | string) { + // modify x in right hand operand with string type itself + return typeof x === "string" && ((x = "hello") && x); // string | number +} +function foo4(x: number | string | boolean) { + return typeof x !== "string" // string | number | boolean + && typeof x !== "number" // number | boolean + && x; // boolean +} +function foo5(x: number | string | boolean) { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + var b: number | boolean; + return typeof x !== "string" // string | number | boolean + && ((b = x) && (typeof x !== "number" // number | boolean + && x)); // boolean +} +function foo6(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + return typeof x !== "string" // string | number | boolean + && (typeof x !== "number" // number | boolean + ? x // boolean + : x === 10) // number +} +function foo7(x: number | string | boolean) { + var y: number| boolean | string; + var z: number| boolean | string; + // Mixing typeguard narrowing + // Assigning value to x deep inside another guard stops narrowing of type too + return typeof x !== "string" + && ((z = x) // string | number | boolean - x changed deeper in conditional expression + && (typeof x === "number" + // change value of x + ? (x = 10 && x.toString()) // number | boolean | string + // do not change value + : (y = x && x.toString()))); // number | boolean | string +} +function foo8(x: number | string) { + // Mixing typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + return typeof x !== "string" + && (x = 10) // change x - number| string + && (typeof x === "number" + ? x // number + : x.length); // string +} + +//// [typeGuardsInRightOperandOfAndAndOperator.js] +// In the right operand of a && operation, +// the type of a variable or parameter is narrowed by any type guard in the left operand when true, +// provided the right operand contains no assignments to the variable or parameter. +function foo(x) { + return typeof x === "string" && x.length === 10; // string +} +function foo2(x) { + // modify x in right hand operand + return typeof x === "string" && ((x = 10) && x); // string | number +} +function foo3(x) { + // modify x in right hand operand with string type itself + return typeof x === "string" && ((x = "hello") && x); // string | number +} +function foo4(x) { + return typeof x !== "string" && typeof x !== "number" && x; // boolean +} +function foo5(x) { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + var b; + return typeof x !== "string" && ((b = x) && (typeof x !== "number" && x)); // boolean +} +function foo6(x) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + return typeof x !== "string" && (typeof x !== "number" ? x : x === 10); // number +} +function foo7(x) { + var y; + var z; + // Mixing typeguard narrowing + // Assigning value to x deep inside another guard stops narrowing of type too + return typeof x !== "string" && ((z = x) && (typeof x === "number" ? (x = 10 && x.toString()) : (y = x && x.toString()))); // number | boolean | string +} +function foo8(x) { + // Mixing typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + return typeof x !== "string" && (x = 10) && (typeof x === "number" ? x : x.length); // string +} diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types new file mode 100644 index 00000000000..884c1cc1f5e --- /dev/null +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types @@ -0,0 +1,215 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts === +// In the right operand of a && operation, +// the type of a variable or parameter is narrowed by any type guard in the left operand when true, +// provided the right operand contains no assignments to the variable or parameter. +function foo(x: number | string) { +>foo : (x: string | number) => boolean +>x : string | number + + return typeof x === "string" && x.length === 10; // string +>typeof x === "string" && x.length === 10 : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>x.length === 10 : boolean +>x.length : number +>x : string +>length : number +} +function foo2(x: number | string) { +>foo2 : (x: string | number) => string | number +>x : string | number + + // modify x in right hand operand + return typeof x === "string" && ((x = 10) && x); // string | number +>typeof x === "string" && ((x = 10) && x) : string | number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>((x = 10) && x) : string | number +>(x = 10) && x : string | number +>(x = 10) : number +>x = 10 : number +>x : string | number +>x : string | number +} +function foo3(x: number | string) { +>foo3 : (x: string | number) => string | number +>x : string | number + + // modify x in right hand operand with string type itself + return typeof x === "string" && ((x = "hello") && x); // string | number +>typeof x === "string" && ((x = "hello") && x) : string | number +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>((x = "hello") && x) : string | number +>(x = "hello") && x : string | number +>(x = "hello") : string +>x = "hello" : string +>x : string | number +>x : string | number +} +function foo4(x: number | string | boolean) { +>foo4 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + return typeof x !== "string" // string | number | boolean +>typeof x !== "string" // string | number | boolean && typeof x !== "number" // number | boolean && x : boolean +>typeof x !== "string" // string | number | boolean && typeof x !== "number" : boolean +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number | boolean + + && typeof x !== "number" // number | boolean +>typeof x !== "number" : boolean +>typeof x : string +>x : number | boolean + + && x; // boolean +>x : boolean +} +function foo5(x: number | string | boolean) { +>foo5 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + var b: number | boolean; +>b : number | boolean + + return typeof x !== "string" // string | number | boolean +>typeof x !== "string" // string | number | boolean && ((b = x) && (typeof x !== "number" // number | boolean && x)) : boolean +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number | boolean + + && ((b = x) && (typeof x !== "number" // number | boolean +>((b = x) && (typeof x !== "number" // number | boolean && x)) : boolean +>(b = x) && (typeof x !== "number" // number | boolean && x) : boolean +>(b = x) : number | boolean +>b = x : number | boolean +>b : number | boolean +>x : number | boolean +>(typeof x !== "number" // number | boolean && x) : boolean +>typeof x !== "number" // number | boolean && x : boolean +>typeof x !== "number" : boolean +>typeof x : string +>x : number | boolean + + && x)); // boolean +>x : boolean +} +function foo6(x: number | string | boolean) { +>foo6 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + // Mixing typeguard narrowing in if statement with conditional expression typeguard + return typeof x !== "string" // string | number | boolean +>typeof x !== "string" // string | number | boolean && (typeof x !== "number" // number | boolean ? x // boolean : x === 10) : boolean +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number | boolean + + && (typeof x !== "number" // number | boolean +>(typeof x !== "number" // number | boolean ? x // boolean : x === 10) : boolean +>typeof x !== "number" // number | boolean ? x // boolean : x === 10 : boolean +>typeof x !== "number" : boolean +>typeof x : string +>x : number | boolean + + ? x // boolean +>x : boolean + + : x === 10) // number +>x === 10 : boolean +>x : number +} +function foo7(x: number | string | boolean) { +>foo7 : (x: string | number | boolean) => string +>x : string | number | boolean + + var y: number| boolean | string; +>y : string | number | boolean + + var z: number| boolean | string; +>z : string | number | boolean + + // Mixing typeguard narrowing + // Assigning value to x deep inside another guard stops narrowing of type too + return typeof x !== "string" +>typeof x !== "string" && ((z = x) // string | number | boolean - x changed deeper in conditional expression && (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()))) : string +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number | boolean + + && ((z = x) // string | number | boolean - x changed deeper in conditional expression +>((z = x) // string | number | boolean - x changed deeper in conditional expression && (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()))) : string +>(z = x) // string | number | boolean - x changed deeper in conditional expression && (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString())) : string +>(z = x) : string | number | boolean +>z = x : string | number | boolean +>z : string | number | boolean +>x : string | number | boolean + + && (typeof x === "number" +>(typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString())) : string +>typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()) : string +>typeof x === "number" : boolean +>typeof x : string +>x : string | number | boolean + + // change value of x + ? (x = 10 && x.toString()) // number | boolean | string +>(x = 10 && x.toString()) : string +>x = 10 && x.toString() : string +>x : string | number | boolean +>10 && x.toString() : string +>x.toString() : string +>x.toString : () => string +>x : string | number | boolean +>toString : () => string + + // do not change value + : (y = x && x.toString()))); // number | boolean | string +>(y = x && x.toString()) : string +>y = x && x.toString() : string +>y : string | number | boolean +>x && x.toString() : string +>x : string | number | boolean +>x.toString() : string +>x.toString : () => string +>x : string | number | boolean +>toString : () => string +} +function foo8(x: number | string) { +>foo8 : (x: string | number) => number +>x : string | number + + // Mixing typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + return typeof x !== "string" +>typeof x !== "string" && (x = 10) // change x - number| string && (typeof x === "number" ? x // number : x.length) : number +>typeof x !== "string" && (x = 10) : number +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number + + && (x = 10) // change x - number| string +>(x = 10) : number +>x = 10 : number +>x : string | number + + && (typeof x === "number" +>(typeof x === "number" ? x // number : x.length) : number +>typeof x === "number" ? x // number : x.length : number +>typeof x === "number" : boolean +>typeof x : string +>x : string | number + + ? x // number +>x : number + + : x.length); // string +>x.length : number +>x : string +>length : number +} diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js new file mode 100644 index 00000000000..d2b7938a9ca --- /dev/null +++ b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js @@ -0,0 +1,96 @@ +//// [typeGuardsInRightOperandOfOrOrOperator.ts] +// In the right operand of a || operation, +// the type of a variable or parameter is narrowed by any type guard in the left operand when false, +// provided the right operand contains no assignments to the variable or parameter. +function foo(x: number | string) { + return typeof x !== "string" || x.length === 10; // string +} +function foo2(x: number | string) { + // modify x in right hand operand + return typeof x !== "string" || ((x = 10) || x); // string | number +} +function foo3(x: number | string) { + // modify x in right hand operand with string type itself + return typeof x !== "string" || ((x = "hello") || x); // string | number +} +function foo4(x: number | string | boolean) { + return typeof x === "string" // string | number | boolean + || typeof x === "number" // number | boolean + || x; // boolean +} +function foo5(x: number | string | boolean) { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + var b: number | boolean; + return typeof x === "string" // string | number | boolean + || ((b = x) || (typeof x === "number" // number | boolean + || x)); // boolean +} +function foo6(x: number | string | boolean) { + // Mixing typeguard + return typeof x === "string" // string | number | boolean + || (typeof x !== "number" // number | boolean + ? x // boolean + : x === 10) // number +} +function foo7(x: number | string | boolean) { + var y: number| boolean | string; + var z: number| boolean | string; + // Mixing typeguard narrowing + // Assigning value to x deep inside another guard stops narrowing of type too + return typeof x === "string" + || ((z = x) // string | number | boolean - x changed deeper in conditional expression + || (typeof x === "number" + // change value of x + ? (x = 10 && x.toString()) // number | boolean | string + // do not change value + : (y = x && x.toString()))); // number | boolean | string +} +function foo8(x: number | string) { + // Mixing typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + return typeof x === "string" + || (x = 10) // change x - number| string + || (typeof x === "number" + ? x // number + : x.length); // string +} + +//// [typeGuardsInRightOperandOfOrOrOperator.js] +// In the right operand of a || operation, +// the type of a variable or parameter is narrowed by any type guard in the left operand when false, +// provided the right operand contains no assignments to the variable or parameter. +function foo(x) { + return typeof x !== "string" || x.length === 10; // string +} +function foo2(x) { + // modify x in right hand operand + return typeof x !== "string" || ((x = 10) || x); // string | number +} +function foo3(x) { + // modify x in right hand operand with string type itself + return typeof x !== "string" || ((x = "hello") || x); // string | number +} +function foo4(x) { + return typeof x === "string" || typeof x === "number" || x; // boolean +} +function foo5(x) { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + var b; + return typeof x === "string" || ((b = x) || (typeof x === "number" || x)); // boolean +} +function foo6(x) { + // Mixing typeguard + return typeof x === "string" || (typeof x !== "number" ? x : x === 10); // number +} +function foo7(x) { + var y; + var z; + // Mixing typeguard narrowing + // Assigning value to x deep inside another guard stops narrowing of type too + return typeof x === "string" || ((z = x) || (typeof x === "number" ? (x = 10 && x.toString()) : (y = x && x.toString()))); // number | boolean | string +} +function foo8(x) { + // Mixing typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + return typeof x === "string" || (x = 10) || (typeof x === "number" ? x : x.length); // string +} diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types new file mode 100644 index 00000000000..8940d50c2d4 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types @@ -0,0 +1,215 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts === +// In the right operand of a || operation, +// the type of a variable or parameter is narrowed by any type guard in the left operand when false, +// provided the right operand contains no assignments to the variable or parameter. +function foo(x: number | string) { +>foo : (x: string | number) => boolean +>x : string | number + + return typeof x !== "string" || x.length === 10; // string +>typeof x !== "string" || x.length === 10 : boolean +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number +>x.length === 10 : boolean +>x.length : number +>x : string +>length : number +} +function foo2(x: number | string) { +>foo2 : (x: string | number) => string | number | boolean +>x : string | number + + // modify x in right hand operand + return typeof x !== "string" || ((x = 10) || x); // string | number +>typeof x !== "string" || ((x = 10) || x) : string | number | boolean +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number +>((x = 10) || x) : string | number +>(x = 10) || x : string | number +>(x = 10) : number +>x = 10 : number +>x : string | number +>x : string | number +} +function foo3(x: number | string) { +>foo3 : (x: string | number) => string | number | boolean +>x : string | number + + // modify x in right hand operand with string type itself + return typeof x !== "string" || ((x = "hello") || x); // string | number +>typeof x !== "string" || ((x = "hello") || x) : string | number | boolean +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number +>((x = "hello") || x) : string | number +>(x = "hello") || x : string | number +>(x = "hello") : string +>x = "hello" : string +>x : string | number +>x : string | number +} +function foo4(x: number | string | boolean) { +>foo4 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + return typeof x === "string" // string | number | boolean +>typeof x === "string" // string | number | boolean || typeof x === "number" // number | boolean || x : boolean +>typeof x === "string" // string | number | boolean || typeof x === "number" : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + || typeof x === "number" // number | boolean +>typeof x === "number" : boolean +>typeof x : string +>x : number | boolean + + || x; // boolean +>x : boolean +} +function foo5(x: number | string | boolean) { +>foo5 : (x: string | number | boolean) => number | boolean +>x : string | number | boolean + + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + var b: number | boolean; +>b : number | boolean + + return typeof x === "string" // string | number | boolean +>typeof x === "string" // string | number | boolean || ((b = x) || (typeof x === "number" // number | boolean || x)) : number | boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + || ((b = x) || (typeof x === "number" // number | boolean +>((b = x) || (typeof x === "number" // number | boolean || x)) : number | boolean +>(b = x) || (typeof x === "number" // number | boolean || x) : number | boolean +>(b = x) : number | boolean +>b = x : number | boolean +>b : number | boolean +>x : number | boolean +>(typeof x === "number" // number | boolean || x) : boolean +>typeof x === "number" // number | boolean || x : boolean +>typeof x === "number" : boolean +>typeof x : string +>x : number | boolean + + || x)); // boolean +>x : boolean +} +function foo6(x: number | string | boolean) { +>foo6 : (x: string | number | boolean) => boolean +>x : string | number | boolean + + // Mixing typeguard + return typeof x === "string" // string | number | boolean +>typeof x === "string" // string | number | boolean || (typeof x !== "number" // number | boolean ? x // boolean : x === 10) : boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + || (typeof x !== "number" // number | boolean +>(typeof x !== "number" // number | boolean ? x // boolean : x === 10) : boolean +>typeof x !== "number" // number | boolean ? x // boolean : x === 10 : boolean +>typeof x !== "number" : boolean +>typeof x : string +>x : number | boolean + + ? x // boolean +>x : boolean + + : x === 10) // number +>x === 10 : boolean +>x : number +} +function foo7(x: number | string | boolean) { +>foo7 : (x: string | number | boolean) => string | number | boolean +>x : string | number | boolean + + var y: number| boolean | string; +>y : string | number | boolean + + var z: number| boolean | string; +>z : string | number | boolean + + // Mixing typeguard narrowing + // Assigning value to x deep inside another guard stops narrowing of type too + return typeof x === "string" +>typeof x === "string" || ((z = x) // string | number | boolean - x changed deeper in conditional expression || (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()))) : string | number | boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean + + || ((z = x) // string | number | boolean - x changed deeper in conditional expression +>((z = x) // string | number | boolean - x changed deeper in conditional expression || (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()))) : string | number | boolean +>(z = x) // string | number | boolean - x changed deeper in conditional expression || (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString())) : string | number | boolean +>(z = x) : string | number | boolean +>z = x : string | number | boolean +>z : string | number | boolean +>x : string | number | boolean + + || (typeof x === "number" +>(typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString())) : string +>typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()) : string +>typeof x === "number" : boolean +>typeof x : string +>x : string | number | boolean + + // change value of x + ? (x = 10 && x.toString()) // number | boolean | string +>(x = 10 && x.toString()) : string +>x = 10 && x.toString() : string +>x : string | number | boolean +>10 && x.toString() : string +>x.toString() : string +>x.toString : () => string +>x : string | number | boolean +>toString : () => string + + // do not change value + : (y = x && x.toString()))); // number | boolean | string +>(y = x && x.toString()) : string +>y = x && x.toString() : string +>y : string | number | boolean +>x && x.toString() : string +>x : string | number | boolean +>x.toString() : string +>x.toString : () => string +>x : string | number | boolean +>toString : () => string +} +function foo8(x: number | string) { +>foo8 : (x: string | number) => number | boolean +>x : string | number + + // Mixing typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + return typeof x === "string" +>typeof x === "string" || (x = 10) // change x - number| string || (typeof x === "number" ? x // number : x.length) : number | boolean +>typeof x === "string" || (x = 10) : number | boolean +>typeof x === "string" : boolean +>typeof x : string +>x : string | number + + || (x = 10) // change x - number| string +>(x = 10) : number +>x = 10 : number +>x : string | number + + || (typeof x === "number" +>(typeof x === "number" ? x // number : x.length) : number +>typeof x === "number" ? x // number : x.length : number +>typeof x === "number" : boolean +>typeof x : string +>x : string | number + + ? x // number +>x : number + + : x.length); // string +>x.length : number +>x : string +>length : number +} diff --git a/tests/baselines/reference/typeGuardsObjectMethods.js b/tests/baselines/reference/typeGuardsObjectMethods.js new file mode 100644 index 00000000000..3bdea2348ab --- /dev/null +++ b/tests/baselines/reference/typeGuardsObjectMethods.js @@ -0,0 +1,93 @@ +//// [typeGuardsObjectMethods.ts] + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var strOrNum: string | number; +var var1: string | number; +var obj1 = { + // Inside method + method(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + + return strOrNum; + }, + get prop() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + }, + set prop(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } +}; +// return expression of the method +strOrNum = typeof obj1.method(strOrNum) === "string" && obj1.method(strOrNum); + +// accessing getter property +strOrNum = typeof obj1.prop === "string" && obj1.prop; + +//// [typeGuardsObjectMethods.js] +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. +// variables in global +var num; +var strOrNum; +var var1; +var obj1 = { + // Inside method + method: function (param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + // parameters in function declaration + num = typeof param === "string" && param.length; // string + return strOrNum; + }, + get prop() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + return strOrNum; + }, + set prop(param) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + // variables in function declaration + var var2; + num = typeof var2 === "string" && var2.length; // string + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } +}; +// return expression of the method +strOrNum = typeof obj1.method(strOrNum) === "string" && obj1.method(strOrNum); +// accessing getter property +strOrNum = typeof obj1.prop === "string" && obj1.prop; diff --git a/tests/baselines/reference/typeGuardsObjectMethods.types b/tests/baselines/reference/typeGuardsObjectMethods.types new file mode 100644 index 00000000000..a063145ce24 --- /dev/null +++ b/tests/baselines/reference/typeGuardsObjectMethods.types @@ -0,0 +1,178 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsObjectMethods.ts === + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +>num : number + +var strOrNum: string | number; +>strOrNum : string | number + +var var1: string | number; +>var1 : string | number + +var obj1 = { +>obj1 : { method: (param: string | number) => string | number; prop: string | number; } +>{ // Inside method method(param: string | number) { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables in function declaration var var2: string | number; num = typeof var2 === "string" && var2.length; // string // parameters in function declaration num = typeof param === "string" && param.length; // string return strOrNum; }, get prop() { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables in function declaration var var2: string | number; num = typeof var2 === "string" && var2.length; // string return strOrNum; }, set prop(param: string | number) { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables in function declaration var var2: string | number; num = typeof var2 === "string" && var2.length; // string // parameters in function declaration num = typeof param === "string" && param.length; // string }} : { method: (param: string | number) => string | number; prop: string | number; } + + // Inside method + method(param: string | number) { +>method : (param: string | number) => string | number +>method(param: string | number) { // global vars in function declaration num = typeof var1 === "string" && var1.length; // string // variables in function declaration var var2: string | number; num = typeof var2 === "string" && var2.length; // string // parameters in function declaration num = typeof param === "string" && param.length; // string return strOrNum; } : (param: string | number) => string | number +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + + return strOrNum; +>strOrNum : string | number + + }, + get prop() { +>prop : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + return strOrNum; +>strOrNum : string | number + + }, + set prop(param: string | number) { +>prop : string | number +>param : string | number + + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string +>num = typeof var1 === "string" && var1.length : number +>num : number +>typeof var1 === "string" && var1.length : number +>typeof var1 === "string" : boolean +>typeof var1 : string +>var1 : string | number +>var1.length : number +>var1 : string +>length : number + + // variables in function declaration + var var2: string | number; +>var2 : string | number + + num = typeof var2 === "string" && var2.length; // string +>num = typeof var2 === "string" && var2.length : number +>num : number +>typeof var2 === "string" && var2.length : number +>typeof var2 === "string" : boolean +>typeof var2 : string +>var2 : string | number +>var2.length : number +>var2 : string +>length : number + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +>num = typeof param === "string" && param.length : number +>num : number +>typeof param === "string" && param.length : number +>typeof param === "string" : boolean +>typeof param : string +>param : string | number +>param.length : number +>param : string +>length : number + } +}; +// return expression of the method +strOrNum = typeof obj1.method(strOrNum) === "string" && obj1.method(strOrNum); +>strOrNum = typeof obj1.method(strOrNum) === "string" && obj1.method(strOrNum) : string | number +>strOrNum : string | number +>typeof obj1.method(strOrNum) === "string" && obj1.method(strOrNum) : string | number +>typeof obj1.method(strOrNum) === "string" : boolean +>typeof obj1.method(strOrNum) : string +>obj1.method(strOrNum) : string | number +>obj1.method : (param: string | number) => string | number +>obj1 : { method: (param: string | number) => string | number; prop: string | number; } +>method : (param: string | number) => string | number +>strOrNum : string | number +>obj1.method(strOrNum) : string | number +>obj1.method : (param: string | number) => string | number +>obj1 : { method: (param: string | number) => string | number; prop: string | number; } +>method : (param: string | number) => string | number +>strOrNum : string | number + +// accessing getter property +strOrNum = typeof obj1.prop === "string" && obj1.prop; +>strOrNum = typeof obj1.prop === "string" && obj1.prop : string | number +>strOrNum : string | number +>typeof obj1.prop === "string" && obj1.prop : string | number +>typeof obj1.prop === "string" : boolean +>typeof obj1.prop : string +>obj1.prop : string | number +>obj1 : { method: (param: string | number) => string | number; prop: string | number; } +>prop : string | number +>obj1.prop : string | number +>obj1 : { method: (param: string | number) => string | number; prop: string | number; } +>prop : string | number + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormExpr1AndExpr2.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormExpr1AndExpr2.ts new file mode 100644 index 00000000000..1ed8e6ebb04 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormExpr1AndExpr2.ts @@ -0,0 +1,46 @@ +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrNumOrBool: string | number | boolean; +var numOrBool: number | boolean; +class C { private p; } +var c: C; +var cOrBool: C| boolean; +var strOrNumOrBoolOrC: string | number | boolean | C; + +// A type guard of the form expr1 && expr2 +// - when true, narrows the type of x by expr1 when true and then by expr2 when true, or +// - when false, narrows the type of x to T1 | T2, where T1 is the type of x narrowed by expr1 when +// false, and T2 is the type of x narrowed by expr1 when true and then by expr2 when false. + +// (typeguard1 && typeguard2) +if (typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number") { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// (typeguard1 && typeguard2 && typeguard3) +if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBoolOrC !== "boolean") { + c = strOrNumOrBoolOrC; // C +} +else { + strOrNumOrBool = strOrNumOrBoolOrC; // string | number | boolean +} +// (typeguard1 && typeguard2 && typeguard11(onAnotherType)) +if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" && typeof strOrNumOrBool === "boolean") { + cOrBool = strOrNumOrBoolOrC; // C | boolean + bool = strOrNumOrBool; // boolean +} +else { + var r1: string | number | boolean | C = strOrNumOrBoolOrC; // string | number | boolean | C + var r2: string | number | boolean = strOrNumOrBool; +} +// (typeguard1) && simpleExpr +if (typeof strOrNumOrBool !== "string" && numOrBool !== strOrNumOrBool) { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + var r3: string | number | boolean = strOrNumOrBool; // string | number | boolean +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormExpr1OrExpr2.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormExpr1OrExpr2.ts new file mode 100644 index 00000000000..1d72f358284 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormExpr1OrExpr2.ts @@ -0,0 +1,46 @@ +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrNumOrBool: string | number | boolean; +var numOrBool: number | boolean; +class C { private p; } +var c: C; +var cOrBool: C| boolean; +var strOrNumOrBoolOrC: string | number | boolean | C; + +// A type guard of the form expr1 || expr2 +// - when true, narrows the type of x to T1 | T2, where T1 is the type of x narrowed by expr1 when true, +// and T2 is the type of x narrowed by expr1 when false and then by expr2 when true, or +// - when false, narrows the type of x by expr1 when false and then by expr2 when false. + +// (typeguard1 || typeguard2) +if (typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number") { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// (typeguard1 || typeguard2 || typeguard3) +if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBoolOrC === "boolean") { + strOrNumOrBool = strOrNumOrBoolOrC; // string | number | boolean +} +else { + c = strOrNumOrBoolOrC; // C +} +// (typeguard1 || typeguard2 || typeguard11(onAnotherType)) +if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "number" || typeof strOrNumOrBool !== "boolean") { + var r1: string | number | boolean | C = strOrNumOrBoolOrC; // string | number | boolean | C + var r2: string | number | boolean = strOrNumOrBool; +} +else { + cOrBool = strOrNumOrBoolOrC; // C | boolean + bool = strOrNumOrBool; // boolean +} +// (typeguard1) || simpleExpr +if (typeof strOrNumOrBool === "string" || numOrBool !== strOrNumOrBool) { + var r3: string | number | boolean = strOrNumOrBool; // string | number | boolean +} +else { + numOrBool = strOrNumOrBool; // number | boolean +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormInstanceOf.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormInstanceOf.ts new file mode 100644 index 00000000000..233909f44aa --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormInstanceOf.ts @@ -0,0 +1,30 @@ +// A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' +// and C has a property named 'prototype' +// - when true, narrows the type of x to the type of the 'prototype' property in C provided +// it is a subtype of the type of x, or +// - when false, has no effect on the type of x. + +class C1 { + p1: string; +} +class C2 { + p2: number; +} +class D1 extends C1 { + p3: number; +} +var str: string; +var num: number; +var strOrNum: string | number; + +var c1Orc2: C1 | C2; +str = c1Orc2 instanceof C1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof C2 && c1Orc2.p2; // C2 +str = c1Orc2 instanceof D1 && c1Orc2.p1; // D1 +num = c1Orc2 instanceof D1 && c1Orc2.p3; // D1 + +var c2Ord1: C2 | D1; +num = c2Ord1 instanceof C2 && c2Ord1.p2; // C2 +num = c2Ord1 instanceof D1 && c2Ord1.p3; // D1 +str = c2Ord1 instanceof D1 && c2Ord1.p1; // D1 +var r2: D1 | C2 = c2Ord1 instanceof C1 && c2Ord1; // C2 | D1 \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormInstanceOfOnInterface.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormInstanceOfOnInterface.ts new file mode 100644 index 00000000000..47e17667bcb --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormInstanceOfOnInterface.ts @@ -0,0 +1,38 @@ +// A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' +// and C has a property named 'prototype' +// - when true, narrows the type of x to the type of the 'prototype' property in C provided +// it is a subtype of the type of x, or +// - when false, has no effect on the type of x. + +interface C1 { + (): C1; + prototype: C1; + p1: string; +} +interface C2 { + (): C2; + prototype: C2; + p2: number; +} +interface D1 extends C1 { + prototype: D1; + p3: number; +} +var str: string; +var num: number; +var strOrNum: string | number; + +var c1: C1; +var c2: C2; +var d1: D1; +var c1Orc2: C1 | C2; +str = c1Orc2 instanceof c1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof c2 && c1Orc2.p2; // C2 +str = c1Orc2 instanceof d1 && c1Orc2.p1; // C1 +num = c1Orc2 instanceof d1 && c1Orc2.p3; // D1 + +var c2Ord1: C2 | D1; +num = c2Ord1 instanceof c2 && c2Ord1.p2; // C2 +num = c2Ord1 instanceof d1 && c2Ord1.p3; // D1 +str = c2Ord1 instanceof d1 && c2Ord1.p1; // D1 +var r2: D1 | C2 = c2Ord1 instanceof c1 && c2Ord1; // C2 | D1 \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormNotExpr.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormNotExpr.ts new file mode 100644 index 00000000000..c9a2cb5fc0b --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormNotExpr.ts @@ -0,0 +1,53 @@ +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrNumOrBool: string | number | boolean; +var numOrBool: number | boolean; + +// A type guard of the form !expr +// - when true, narrows the type of x by expr when false, or +// - when false, narrows the type of x by expr when true. + +// !typeguard1 +if (!(typeof strOrNum === "string")) { + num === strOrNum; // number +} +else { + str = strOrNum; // string +} +// !(typeguard1 || typeguard2) +if (!(typeof strOrNumOrBool === "string" || typeof strOrNumOrBool === "number")) { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// !(typeguard1) || !(typeguard2) +if (!(typeof strOrNumOrBool !== "string") || !(typeof strOrNumOrBool !== "number")) { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// !(typeguard1 && typeguard2) +if (!(typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number")) { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +// !(typeguard1) && !(typeguard2) +if (!(typeof strOrNumOrBool === "string") && !(typeof strOrNumOrBool === "number")) { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +// !(typeguard1) && simpleExpr +if (!(typeof strOrNumOrBool === "string") && numOrBool !== strOrNumOrBool) { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfBoolean.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfBoolean.ts new file mode 100644 index 00000000000..dc6166852ae --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfBoolean.ts @@ -0,0 +1,82 @@ +class C { private p: string }; + +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrNumOrBool: string | number | boolean; +var strOrC: string | C; +var numOrC: number | C; +var boolOrC: boolean | C; +var c: C; + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "boolean") { + bool = strOrNum; // boolean +} +else { + var z: string | number = strOrNum; // string | number +} +if (typeof strOrBool === "boolean") { + bool = strOrBool; // boolean +} +else { + str = strOrBool; // string +} +if (typeof numOrBool === "boolean") { + bool = numOrBool; // boolean +} +else { + num = numOrBool; // number +} +if (typeof strOrNumOrBool === "boolean") { + bool = strOrNumOrBool; // boolean +} +else { + strOrNum = strOrNumOrBool; // string | number +} +if (typeof boolOrC === "boolean") { + bool = boolOrC; // boolean +} +else { + c = boolOrC; // C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "boolean") { + var z: string | number = strOrNum; // string | number +} +else { + bool = strOrNum; // boolean +} +if (typeof strOrBool !== "boolean") { + str = strOrBool; // string +} +else { + bool = strOrBool; // boolean +} +if (typeof numOrBool !== "boolean") { + num = numOrBool; // number +} +else { + bool = numOrBool; // boolean +} +if (typeof strOrNumOrBool !== "boolean") { + strOrNum = strOrNumOrBool; // string | number +} +else { + bool = strOrNumOrBool; // boolean +} +if (typeof boolOrC !== "boolean") { + c = boolOrC; // C +} +else { + bool = boolOrC; // boolean +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfEqualEqualHasNoEffect.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfEqualEqualHasNoEffect.ts new file mode 100644 index 00000000000..03d650ad626 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfEqualEqualHasNoEffect.ts @@ -0,0 +1,35 @@ +class C { private p: string }; + +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrC: string | C; + +// typeof x == s has not effect on typeguard +if (typeof strOrNum == "string") { + var r1 = strOrNum; // string | number +} +else { + var r1 = strOrNum; // string | number +} + +if (typeof strOrBool == "boolean") { + var r2 = strOrBool; // string | boolean +} +else { + var r2 = strOrBool; // string | boolean +} + +if (typeof numOrBool == "number") { + var r3 = numOrBool; // number | boolean +} +else { + var r3 = numOrBool; // number | boolean +} + +if (typeof strOrC == "Object") { + var r4 = strOrC; // string | C +} +else { + var r4 = strOrC; // string | C +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNotEqualHasNoEffect.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNotEqualHasNoEffect.ts new file mode 100644 index 00000000000..19d5495fcbb --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNotEqualHasNoEffect.ts @@ -0,0 +1,35 @@ +class C { private p: string }; + +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrC: string | C; + +// typeof x != s has not effect on typeguard +if (typeof strOrNum != "string") { + var r1 = strOrNum; // string | number +} +else { + var r1 = strOrNum; // string | number +} + +if (typeof strOrBool != "boolean") { + var r2 = strOrBool; // string | boolean +} +else { + var r2 = strOrBool; // string | boolean +} + +if (typeof numOrBool != "number") { + var r3 = numOrBool; // number | boolean +} +else { + var r3 = numOrBool; // number | boolean +} + +if (typeof strOrC != "Object") { + var r4 = strOrC; // string | C +} +else { + var r4 = strOrC; // string | C +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNumber.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNumber.ts new file mode 100644 index 00000000000..b05e5a3a002 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNumber.ts @@ -0,0 +1,82 @@ +class C { private p: string }; + +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrNumOrBool: string | number | boolean; +var strOrC: string | C; +var numOrC: number | C; +var boolOrC: boolean | C; +var c: C; + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "number") { + num = strOrNum; // number +} +else { + str === strOrNum; // string +} +if (typeof strOrBool === "number") { + num = strOrBool; // number +} +else { + var y: string | boolean = strOrBool; // string | boolean +} +if (typeof numOrBool === "number") { + num = numOrBool; // number +} +else { + var x: number | boolean = numOrBool; // number | boolean +} +if (typeof strOrNumOrBool === "number") { + num = strOrNumOrBool; // number +} +else { + strOrBool = strOrNumOrBool; // string | boolean +} +if (typeof numOrC === "number") { + num = numOrC; // number +} +else { + c = numOrC; // C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "number") { + str === strOrNum; // string +} +else { + num = strOrNum; // number +} +if (typeof strOrBool !== "number") { + var y: string | boolean = strOrBool; // string | boolean +} +else { + num = strOrBool; // number +} +if (typeof numOrBool !== "number") { + var x: number | boolean = numOrBool; // number | boolean +} +else { + num = numOrBool; // number +} +if (typeof strOrNumOrBool !== "number") { + strOrBool = strOrNumOrBool; // string | boolean +} +else { + num = strOrNumOrBool; // number +} +if (typeof numOrC !== "number") { + c = numOrC; // C +} +else { + num = numOrC; // number +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts new file mode 100644 index 00000000000..18109890c27 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts @@ -0,0 +1,72 @@ +class C { private p: string }; + +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrNumOrBool: string | number | boolean; +var strOrC: string | C; +var numOrC: number | C; +var boolOrC: boolean | C; +var emptyObj: {}; +var c: C; + +// A type guard of the form typeof x === s, +// where s is a string literal with any value but 'string', 'number' or 'boolean', +// - when true, removes the primitive types string, number, and boolean from the type of x, or +// - when false, has no effect on the type of x. + +if (typeof strOrNumOrBool === "Object") { + emptyObj = strOrNumOrBool; // {} +} +else { + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +} +if (typeof strOrC === "Object") { + c = strOrC; // C +} +else { + var r2: string | C = strOrC; // string | C +} +if (typeof numOrC === "Object") { + c = numOrC; // C +} +else { + var r3: number | C = numOrC; // number | C +} +if (typeof boolOrC === "Object") { + c = boolOrC; // C +} +else { + var r4: boolean | C = boolOrC; // boolean | C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNumOrBool !== "Object") { + var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean +} +else { + emptyObj = strOrNumOrBool; // {} +} +if (typeof strOrC !== "Object") { + var r2: string | C = strOrC; // string | C +} +else { + c = strOrC; // C +} +if (typeof numOrC !== "Object") { + var r3: number | C = numOrC; // number | C +} +else { + c = numOrC; // C +} +if (typeof boolOrC !== "Object") { + var r4: boolean | C = boolOrC; // boolean | C +} +else { + c = boolOrC; // C +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfString.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfString.ts new file mode 100644 index 00000000000..8c6bbdaf907 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfString.ts @@ -0,0 +1,82 @@ +class C { private p: string }; + +var str: string; +var bool: boolean; +var num: number; +var strOrNum: string | number; +var strOrBool: string | boolean; +var numOrBool: number | boolean +var strOrNumOrBool: string | number | boolean; +var strOrC: string | C; +var numOrC: number | C; +var boolOrC: boolean | C; +var c: C; + +// A type guard of the form typeof x === s, +// where s is a string literal with the value 'string', 'number', or 'boolean', +// - when true, narrows the type of x to the given primitive type, or +// - when false, removes the primitive type from the type of x. +if (typeof strOrNum === "string") { + str = strOrNum; // string +} +else { + num === strOrNum; // number +} +if (typeof strOrBool === "string") { + str = strOrBool; // string +} +else { + bool = strOrBool; // boolean +} +if (typeof numOrBool === "string") { + str = numOrBool; // string +} +else { + var x : number | boolean = numOrBool; // number | boolean +} +if (typeof strOrNumOrBool === "string") { + str = strOrNumOrBool; // string +} +else { + numOrBool = strOrNumOrBool; // number | boolean +} +if (typeof strOrC === "string") { + str = strOrC; // string +} +else { + c = strOrC; // C +} + +// A type guard of the form typeof x !== s, where s is a string literal, +// - 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 strOrNum !== "string") { + num === strOrNum; // number +} +else { + str = strOrNum; // string +} +if (typeof strOrBool !== "string") { + bool = strOrBool; // boolean +} +else { + str = strOrBool; // string +} +if (typeof numOrBool !== "string") { + var x: number | boolean = numOrBool; // number | boolean +} +else { + str = numOrBool; // string +} +if (typeof strOrNumOrBool !== "string") { + numOrBool = strOrNumOrBool; // number | boolean +} +else { + str = strOrNumOrBool; // string +} +if (typeof strOrC !== "string") { + c = strOrC; // C +} +else { + str = strOrC; // string +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts new file mode 100644 index 00000000000..9c78e725397 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts @@ -0,0 +1,36 @@ +// Also note that it is possible to defeat a type guard by calling a function that changes the +// type of the guarded variable. +function foo(x: number | string) { + function f() { + x = 10; + } + if (typeof x === "string") { + f(); + return x.length; // string + } + else { + return x++; // number + } +} +function foo2(x: number | string) { + if (typeof x === "string") { + return x.length; // string + } + else { + (function f() { + x = 10; + })(); + return x++; // number + } +} +function foo3(x: number | string) { + if (typeof x === "string") { + return x.length; // string + } + else { + (() => { + x = 10; + })(); + return x++; // number + } +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInClassAccessors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInClassAccessors.ts new file mode 100644 index 00000000000..04b9a6a394e --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInClassAccessors.ts @@ -0,0 +1,103 @@ +//@target: es5 + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var strOrNum: string | number; +var var1: string | number; +class ClassWithAccessors { + // Inside public accessor getter + get p1() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + } + // Inside public accessor setter + set p1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // parameter of function declaration + num = typeof param === "string" && param.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + } + // Inside private accessor getter + private get pp1() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + } + // Inside private accessor setter + private set pp1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // parameter of function declaration + num = typeof param === "string" && param.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + } + // Inside static accessor getter + static get s1() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + } + // Inside static accessor setter + static set s1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // parameter of function declaration + num = typeof param === "string" && param.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + } + // Inside private static accessor getter + private static get ss1() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + } + // Inside private static accessor setter + private static set ss1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // parameter of function declaration + num = typeof param === "string" && param.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + } +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInClassMethods.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInClassMethods.ts new file mode 100644 index 00000000000..cb1327027c5 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInClassMethods.ts @@ -0,0 +1,67 @@ +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var var1: string | number; +class C1 { + constructor(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + private p1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + p2(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + private static s1(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } + // Inside function declaration + static s2(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts new file mode 100644 index 00000000000..1633c80ab67 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts @@ -0,0 +1,97 @@ +// In the true expression of a conditional expression, +// the type of a variable or parameter is narrowed by any type guard in the condition when true, +// provided the true expression contains no assignments to the variable or parameter. +// In the false expression of a conditional expression, +// the type of a variable or parameter is narrowed by any type guard in the condition when false, +// provided the false expression contains no assignments to the variable or parameter. + +function foo(x: number | string) { + return typeof x === "string" + ? x.length // string + : x++; // number +} +function foo2(x: number | string) { + // x is assigned in the if true branch, the type is not narrowed + return typeof x === "string" + ? (x = 10 && x)// string | number + : x; // string | number +} +function foo3(x: number | string) { + // x is assigned in the if false branch, the type is not narrowed + // even though assigned using same type as narrowed expression + return typeof x === "string" + ? (x = "Hello" && x) // string | number + : x; // string | number +} +function foo4(x: number | string) { + // false branch updates the variable - so here it is not number + // even though assigned using same type as narrowed expression + return typeof x === "string" + ? x // string | number + : (x = 10 && x); // string | number +} +function foo5(x: number | string) { + // false branch updates the variable - so here it is not number + return typeof x === "string" + ? x // string | number + : (x = "hello" && x); // string | number +} +function foo6(x: number | string) { + // Modify in both branches + return typeof x === "string" + ? (x = 10 && x) // string | number + : (x = "hello" && x); // string | number +} +function foo7(x: number | string | boolean) { + return typeof x === "string" + ? x === "hello" // string + : typeof x === "boolean" + ? x // boolean + : x == 10; // number +} +function foo8(x: number | string | boolean) { + var b: number | boolean; + return typeof x === "string" + ? x === "hello" + : ((b = x) && // number | boolean + (typeof x === "boolean" + ? x // boolean + : x == 10)); // number +} +function foo9(x: number | string) { + var y = 10; + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + return typeof x === "string" + ? ((y = x.length) && x === "hello") // string + : x === 10; // number +} +function foo10(x: number | string | boolean) { + // Mixing typeguards + var b: boolean | number; + return typeof x === "string" + ? x // string + : ((b = x) // x is number | boolean + && typeof x === "number" + && x.toString()); // x is number +} +function foo11(x: number | string | boolean) { + // Mixing typeguards + // Assigning value to x deep inside another guard stops narrowing of type too + var b: number | boolean | string; + return typeof x === "string" + ? x // number | boolean | string - changed in the false branch + : ((b = x) // x is number | boolean | string - because the assignment changed it + && typeof x === "number" + && (x = 10) // assignment to x + && x); // x is number | boolean | string +} +function foo12(x: number | string | boolean) { + // Mixing typeguards + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + var b: number | boolean | string; + return typeof x === "string" + ? (x = 10 && x.toString().length) // number | boolean | string - changed here + : ((b = x) // x is number | boolean | string - changed in true branch + && typeof x === "number" + && x); // x is number +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInExternalModule.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInExternalModule.ts new file mode 100644 index 00000000000..aa41a93f4ed --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInExternalModule.ts @@ -0,0 +1,24 @@ +//@module: commonjs +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// local variable in external module +var num: number; +var var1: string | number; +if (typeof var1 === "string") { + num = var1.length; // string +} +else { + num = var1; // number +} + +// exported variable in external module +var strOrNum: string | number; +export var var2: string | number; +if (typeof var2 === "string") { + // export makes the var property and not variable + strOrNum = var2; // string | number +} +else { + strOrNum = var2; // number | string +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunction.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunction.ts new file mode 100644 index 00000000000..a374a248071 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunction.ts @@ -0,0 +1,87 @@ +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var var1: string | number; +// Inside function declaration +function f(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string +} +// local function declaration +function f1(param: string | number) { + var var2: string | number; + function f2(param1: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + + // local + var var3: string | number; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + } +} +// Function expression +function f2(param: string | number) { + // variables in function declaration + var var2: string | number; + // variables in function expressions + var r = function (param1: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + + // local + var var3: string | number; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + } (param); +} +// Arrow expression +function f3(param: string | number) { + // variables in function declaration + var var2: string | number; + // variables in function expressions + var r = ((param1: string | number) => { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables from outer function declaration + num = typeof var2 === "string" && var2.length; // string + + // parameters in outer declaration + num = typeof param === "string" && param.length; // string + + // local + var var3: string | number; + num = typeof var3 === "string" && var3.length; // string + num = typeof param1 === "string" && param1.length; // string + })(param); +} +// Return type of function +// Inside function declaration +var strOrNum: string | number; +function f4() { + var var2: string | number = strOrNum; + return var2; +} +strOrNum = typeof f4() === "string" && f4(); // string | number \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts new file mode 100644 index 00000000000..244e9f8fa64 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts @@ -0,0 +1,70 @@ +// typeguards are scoped in function/module block + +function foo(x: number | string | boolean) { + return typeof x === "string" + ? x + : function f() { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number | string + } (); +} +function foo2(x: number | string | boolean) { + return typeof x === "string" + ? x + : function f(a: number | boolean) { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number | string + } (x); // x here is narrowed to number | boolean +} +function foo3(x: number | string | boolean) { + return typeof x === "string" + ? x + : (() => { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number | string + })(); +} +function foo4(x: number | string | boolean) { + return typeof x === "string" + ? x + : ((a: number | boolean) => { + var b = x; // new scope - number | boolean | string + return typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number | string + })(x); // x here is narrowed to number | boolean +} +module m { + var x: number | string | boolean; + module m2 { + var b = x; // new scope - number | boolean | string + var y: string; + if (typeof x === "string") { + y = x // string; + } else { + y = typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number + } + } +} +module m1 { + var x: number | string | boolean; + module m2.m3 { + var b = x; // new scope - number | boolean | string + var y: string; + if (typeof x === "string") { + y = x // string; + } else { + y = typeof x === "boolean" + ? x.toString() // boolean + : x.toString(); // number + } + } +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInGlobal.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInGlobal.ts new file mode 100644 index 00000000000..329442751df --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInGlobal.ts @@ -0,0 +1,12 @@ +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var var1: string | number; +if (typeof var1 === "string") { + num = var1.length; // string +} +else { + num = var1; // number +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts new file mode 100644 index 00000000000..1b1e3cfab6c --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts @@ -0,0 +1,148 @@ +// In the true branch statement of an ‘if’ statement, +// the type of a variable or parameter is narrowed by any type guard in the ‘if’ condition when true, +// provided the true branch statement contains no assignments to the variable or parameter. +// In the false branch statement of an ‘if’ statement, +// the type of a variable or parameter is narrowed by any type guard in the ‘if’ condition when false, +// provided the false branch statement contains no assignments to the variable or parameter +function foo(x: number | string) { + if (typeof x === "string") { + return x.length; // string + } + else { + return x++; // number + } +} +function foo2(x: number | string) { + // x is assigned in the if true branch, the type is not narrowed + if (typeof x === "string") { + x = 10; + return x; // string | number + } + else { + return x; // string | number + } +} +function foo3(x: number | string) { + // x is assigned in the if true branch, the type is not narrowed + if (typeof x === "string") { + x = "Hello"; // even though assigned using same type as narrowed expression + return x; // string | number + } + else { + return x; // string | number + } +} +function foo4(x: number | string) { + // false branch updates the variable - so here it is not number + if (typeof x === "string") { + return x; // string | number + } + else { + x = 10; // even though assigned number - this should result in x to be string | number + return x; // string | number + } +} +function foo5(x: number | string) { + // false branch updates the variable - so here it is not number + if (typeof x === "string") { + return x; // string | number + } + else { + x = "hello"; + return x; // string | number + } +} +function foo6(x: number | string) { + // Modify in both branches + if (typeof x === "string") { + x = 10; + return x; // string | number + } + else { + x = "hello"; + return x; // string | number + } +} +function foo7(x: number | string | boolean) { + if (typeof x === "string") { + return x === "hello"; // string + } + else if (typeof x === "boolean") { + return x; // boolean + } + else { + return x == 10; // number + } +} +function foo8(x: number | string | boolean) { + if (typeof x === "string") { + return x === "hello"; // string + } + else { + var b: number | boolean = x; // number | boolean + if (typeof x === "boolean") { + return x; // boolean + } + else { + return x == 10; // number + } + } +} +function foo9(x: number | string) { + var y = 10; + if (typeof x === "string") { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + y = x.length; + return x === "hello"; // string + } + else { + return x == 10; // number + } +} +function foo10(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + if (typeof x === "string") { + return x === "hello"; // string + } + else { + var y: boolean | string; + var b = x; // number | boolean + return typeof x === "number" + ? x === 10 // number + : x; // x should be boolean + } +} +function foo11(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x deep inside another guard stops narrowing of type too + if (typeof x === "string") { + return x; // string | number | boolean - x changed in else branch + } + else { + var y: number| boolean | string; + var b = x; // number | boolean | string - because below we are changing value of x in if statement + return typeof x === "number" + ? ( + // change value of x + x = 10 && x.toString() // number | boolean | string + ) + : ( + // do not change value + y = x && x.toString() // number | boolean | string + ); + } +} +function foo12(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + if (typeof x === "string") { + return x.toString(); // string | number | boolean - x changed in else branch + } + else { + x = 10; + var b = x; // number | boolean | string + return typeof x === "number" + ? x.toString() // number + : x.toString(); // boolean | string + } +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInModule.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInModule.ts new file mode 100644 index 00000000000..fd5f432ebdb --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInModule.ts @@ -0,0 +1,86 @@ +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var strOrNum: string | number; +var var1: string | number; +// Inside module +module m1 { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in module declaration + var var2: string | number; + if (typeof var2 === "string") { + num = var2.length; // string + } + else { + num = var2; // number + } + + // exported variable in the module + export var var3: string | number; + if (typeof var3 === "string") { + strOrNum = var3; // string | number + } + else { + strOrNum = var3; // string | number + } +} +// local module +module m2 { + var var2: string | number; + export var var3: string | number; + module m3 { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // local variables from outer module declaration + num = typeof var2 === "string" && var2.length; // string + + // exported variable from outer the module + strOrNum = typeof var3 === "string" && var3; // string | number + + // variables in module declaration + var var4: string | number; + if (typeof var4 === "string") { + num = var4.length; // string + } + else { + num = var4; // number + } + + // exported variable in the module + export var var5: string | number; + if (typeof var5 === "string") { + strOrNum = var5; // string | number + } + else { + strOrNum = var5; // string | number + } + } +} +// Dotted module +module m3.m4 { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in module declaration + var var2: string | number; + if (typeof var2 === "string") { + num = var2.length; // string + } + else { + num = var2; // number + } + + // exported variable in the module + export var var3: string | number; + if (typeof var3 === "string") { + strOrNum = var3; // string | number + } + else { + strOrNum = var3; // string | number + } +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInProperties.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInProperties.ts new file mode 100644 index 00000000000..bffe8d3d1b8 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInProperties.ts @@ -0,0 +1,27 @@ +//@target: es5 + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +var num: number; +var strOrNum: string | number; +class C1 { + private pp1: string | number; + pp2: string | number; + // Inside public accessor getter + get pp3() { + return strOrNum; + } + method() { + strOrNum = typeof this.pp1 === "string" && this.pp1; // string | number + strOrNum = typeof this.pp2 === "string" && this.pp2; // string | number + strOrNum = typeof this.pp3 === "string" && this.pp3; // string | number + } +} +var c1: C1; +strOrNum = typeof c1.pp2 === "string" && c1.pp2; // string | number +strOrNum = typeof c1.pp3 === "string" && c1.pp3; // string | number +var obj1: { + x: string | number; +}; +strOrNum = typeof obj1.x === "string" && obj1.x; // string | number \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts new file mode 100644 index 00000000000..da0d8ce24b5 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts @@ -0,0 +1,55 @@ +// In the right operand of a && operation, +// the type of a variable or parameter is narrowed by any type guard in the left operand when true, +// provided the right operand contains no assignments to the variable or parameter. +function foo(x: number | string) { + return typeof x === "string" && x.length === 10; // string +} +function foo2(x: number | string) { + // modify x in right hand operand + return typeof x === "string" && ((x = 10) && x); // string | number +} +function foo3(x: number | string) { + // modify x in right hand operand with string type itself + return typeof x === "string" && ((x = "hello") && x); // string | number +} +function foo4(x: number | string | boolean) { + return typeof x !== "string" // string | number | boolean + && typeof x !== "number" // number | boolean + && x; // boolean +} +function foo5(x: number | string | boolean) { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + var b: number | boolean; + return typeof x !== "string" // string | number | boolean + && ((b = x) && (typeof x !== "number" // number | boolean + && x)); // boolean +} +function foo6(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + return typeof x !== "string" // string | number | boolean + && (typeof x !== "number" // number | boolean + ? x // boolean + : x === 10) // number +} +function foo7(x: number | string | boolean) { + var y: number| boolean | string; + var z: number| boolean | string; + // Mixing typeguard narrowing + // Assigning value to x deep inside another guard stops narrowing of type too + return typeof x !== "string" + && ((z = x) // string | number | boolean - x changed deeper in conditional expression + && (typeof x === "number" + // change value of x + ? (x = 10 && x.toString()) // number | boolean | string + // do not change value + : (y = x && x.toString()))); // number | boolean | string +} +function foo8(x: number | string) { + // Mixing typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + return typeof x !== "string" + && (x = 10) // change x - number| string + && (typeof x === "number" + ? x // number + : x.length); // string +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts new file mode 100644 index 00000000000..867dc143a85 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts @@ -0,0 +1,55 @@ +// In the right operand of a || operation, +// the type of a variable or parameter is narrowed by any type guard in the left operand when false, +// provided the right operand contains no assignments to the variable or parameter. +function foo(x: number | string) { + return typeof x !== "string" || x.length === 10; // string +} +function foo2(x: number | string) { + // modify x in right hand operand + return typeof x !== "string" || ((x = 10) || x); // string | number +} +function foo3(x: number | string) { + // modify x in right hand operand with string type itself + return typeof x !== "string" || ((x = "hello") || x); // string | number +} +function foo4(x: number | string | boolean) { + return typeof x === "string" // string | number | boolean + || typeof x === "number" // number | boolean + || x; // boolean +} +function foo5(x: number | string | boolean) { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + var b: number | boolean; + return typeof x === "string" // string | number | boolean + || ((b = x) || (typeof x === "number" // number | boolean + || x)); // boolean +} +function foo6(x: number | string | boolean) { + // Mixing typeguard + return typeof x === "string" // string | number | boolean + || (typeof x !== "number" // number | boolean + ? x // boolean + : x === 10) // number +} +function foo7(x: number | string | boolean) { + var y: number| boolean | string; + var z: number| boolean | string; + // Mixing typeguard narrowing + // Assigning value to x deep inside another guard stops narrowing of type too + return typeof x === "string" + || ((z = x) // string | number | boolean - x changed deeper in conditional expression + || (typeof x === "number" + // change value of x + ? (x = 10 && x.toString()) // number | boolean | string + // do not change value + : (y = x && x.toString()))); // number | boolean | string +} +function foo8(x: number | string) { + // Mixing typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + return typeof x === "string" + || (x = 10) // change x - number| string + || (typeof x === "number" + ? x // number + : x.length); // string +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsObjectMethods.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsObjectMethods.ts new file mode 100644 index 00000000000..bbf68189058 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsObjectMethods.ts @@ -0,0 +1,51 @@ +//@target: es5 + +// Note that type guards affect types of variables and parameters only and +// have no effect on members of objects such as properties. + +// variables in global +var num: number; +var strOrNum: string | number; +var var1: string | number; +var obj1 = { + // Inside method + method(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + + return strOrNum; + }, + get prop() { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + return strOrNum; + }, + set prop(param: string | number) { + // global vars in function declaration + num = typeof var1 === "string" && var1.length; // string + + // variables in function declaration + var var2: string | number; + num = typeof var2 === "string" && var2.length; // string + + // parameters in function declaration + num = typeof param === "string" && param.length; // string + } +}; +// return expression of the method +strOrNum = typeof obj1.method(strOrNum) === "string" && obj1.method(strOrNum); + +// accessing getter property +strOrNum = typeof obj1.prop === "string" && obj1.prop; \ No newline at end of file