From 33cdc2f876d706e3e4a6826283af7fac05e3644c Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 5 Nov 2014 16:04:06 -0800 Subject: [PATCH] =?UTF-8?q?Type=20guards=20in=20||=20operator=20=E2=80=A2?= =?UTF-8?q?=09In=20the=20right=20operand=20of=20a=20||=20operation,=20the?= =?UTF-8?q?=20type=20of=20a=20variable=20or=20parameter=20is=20narrowed=20?= =?UTF-8?q?by=20any=20type=20guard=20in=20the=20left=20operand=20when=20fa?= =?UTF-8?q?lse,=20provided=20the=20right=20operand=20contains=20no=20assig?= =?UTF-8?q?nments=20to=20the=20variable=20or=20parameter.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../typeGuardsInRightOperandOfOrOrOperator.js | 96 ++++++++ ...peGuardsInRightOperandOfOrOrOperator.types | 215 ++++++++++++++++++ .../typeGuardsInRightOperandOfOrOrOperator.ts | 55 +++++ 3 files changed, 366 insertions(+) create mode 100644 tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js create mode 100644 tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js new file mode 100644 index 0000000000..d2b7938a9c --- /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 0000000000..8940d50c2d --- /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/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts new file mode 100644 index 0000000000..867dc143a8 --- /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