From 11912e8fde36b5581864e280ad7176b8f86d9bbc Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 5 Nov 2014 15:55:30 -0800 Subject: [PATCH] =?UTF-8?q?TypeGuards=20in=20right=20operand=20of=20&&=20o?= =?UTF-8?q?peration=20=E2=80=A2=09In=20the=20right=20operand=20of=20a=20&&?= =?UTF-8?q?=20operation,=20the=20type=20of=20a=20variable=20or=20parameter?= =?UTF-8?q?=20is=20narrowed=20by=20any=20type=20guard=20in=20the=20left=20?= =?UTF-8?q?operand=20when=20true,=20provided=20the=20right=20operand=20con?= =?UTF-8?q?tains=20no=20assignments=20to=20the=20variable=20or=20parameter?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ypeGuardsInRightOperandOfAndAndOperator.js | 96 ++++++++ ...GuardsInRightOperandOfAndAndOperator.types | 215 ++++++++++++++++++ ...ypeGuardsInRightOperandOfAndAndOperator.ts | 55 +++++ 3 files changed, 366 insertions(+) create mode 100644 tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js create mode 100644 tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts 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/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