Type guards in || operator

•	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.
This commit is contained in:
Sheetal Nandi 2014-11-05 16:04:06 -08:00
parent 11912e8fde
commit 33cdc2f876
3 changed files with 366 additions and 0 deletions

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}