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/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