From a57ee29ff7f9ddef02f12d4540928b11adc9333f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 8 Jun 2016 11:41:44 -0700 Subject: [PATCH] Add tests --- .../reference/typeGuardIntersectionTypes.js | 181 ++++++++++ .../typeGuardIntersectionTypes.symbols | 262 +++++++++++++++ .../typeGuardIntersectionTypes.types | 310 ++++++++++++++++++ .../typeGuards/typeGuardIntersectionTypes.ts | 115 +++++++ 4 files changed, 868 insertions(+) create mode 100644 tests/baselines/reference/typeGuardIntersectionTypes.js create mode 100644 tests/baselines/reference/typeGuardIntersectionTypes.symbols create mode 100644 tests/baselines/reference/typeGuardIntersectionTypes.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.js b/tests/baselines/reference/typeGuardIntersectionTypes.js new file mode 100644 index 00000000000..16d35c43889 --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.js @@ -0,0 +1,181 @@ +//// [typeGuardIntersectionTypes.ts] + +interface X { + x: string; +} + +interface Y { + y: string; +} + +interface Z { + z: string; +} + +declare function isX(obj: any): obj is X; +declare function isY(obj: any): obj is Y; +declare function isZ(obj: any): obj is Z; + +function f1(obj: Object) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} + +// Repro from #8911 + +// two interfaces +interface A { + a: string; +} + +interface B { + b: string; +} + +// a type guard for B +function isB(toTest: any): toTest is B { + return toTest && toTest.b; +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { + if (isB(a)) { + return a; + } else { + return null; + } +} + +// Repro from #9011 + +declare function log(s: string): void; + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +interface Legged { legs: number; } +interface Winged { wings: boolean; } + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { + + // All beasts with legs + if (hasLegs(beast)) { + + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. + log(`pegasus - 4 legs, wings`); + } + else if (beast.legs === 2) { // ERROR TS2339... + log(`bird - 2 legs, wings`); + } + else { + log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { + log(`quetzalcoatl - no legs, wings`) + } + else { + log(`snake - no legs, no wings`) + } + } +} + +function beastFoo(beast: Object) { + if (hasWings(beast) && hasLegs(beast)) { + beast // beast is Legged + // ideally, beast would be Winged && Legged here... + } + else { + beast + } + + if (hasLegs(beast) && hasWings(beast)) { + beast // beast is Winged + // ideally, beast would be Legged && Winged here... + } +} + +//// [typeGuardIntersectionTypes.js] +function f1(obj) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} +// a type guard for B +function isB(toTest) { + return toTest && toTest.b; +} +// a function that turns an A into an A & B +function union(a) { + if (isB(a)) { + return a; + } + else { + return null; + } +} +// Beast feature detection via user-defined type guards +function hasLegs(x) { return x && typeof x.legs === 'number'; } +function hasWings(x) { return x && !!x.wings; } +// Function to identify a given beast by detecting its features +function identifyBeast(beast) { + // All beasts with legs + if (hasLegs(beast)) { + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { + log("pegasus - 4 legs, wings"); + } + else if (beast.legs === 2) { + log("bird - 2 legs, wings"); + } + else { + log("unknown - " + beast.legs + " legs, wings"); // ERROR TS2339... + } + } + else { + log("manbearpig - " + beast.legs + " legs, no wings"); + } + } + else { + if (hasWings(beast)) { + log("quetzalcoatl - no legs, wings"); + } + else { + log("snake - no legs, no wings"); + } + } +} +function beastFoo(beast) { + if (hasWings(beast) && hasLegs(beast)) { + beast; // beast is Legged + } + else { + beast; + } + if (hasLegs(beast) && hasWings(beast)) { + beast; // beast is Winged + } +} diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.symbols b/tests/baselines/reference/typeGuardIntersectionTypes.symbols new file mode 100644 index 00000000000..c59d972cd30 --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.symbols @@ -0,0 +1,262 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts === + +interface X { +>X : Symbol(X, Decl(typeGuardIntersectionTypes.ts, 0, 0)) + + x: string; +>x : Symbol(X.x, Decl(typeGuardIntersectionTypes.ts, 1, 13)) +} + +interface Y { +>Y : Symbol(Y, Decl(typeGuardIntersectionTypes.ts, 3, 1)) + + y: string; +>y : Symbol(Y.y, Decl(typeGuardIntersectionTypes.ts, 5, 13)) +} + +interface Z { +>Z : Symbol(Z, Decl(typeGuardIntersectionTypes.ts, 7, 1)) + + z: string; +>z : Symbol(Z.z, Decl(typeGuardIntersectionTypes.ts, 9, 13)) +} + +declare function isX(obj: any): obj is X; +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 13, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 13, 21)) +>X : Symbol(X, Decl(typeGuardIntersectionTypes.ts, 0, 0)) + +declare function isY(obj: any): obj is Y; +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 14, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 14, 21)) +>Y : Symbol(Y, Decl(typeGuardIntersectionTypes.ts, 3, 1)) + +declare function isZ(obj: any): obj is Z; +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 15, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 15, 21)) +>Z : Symbol(Z, Decl(typeGuardIntersectionTypes.ts, 7, 1)) + +function f1(obj: Object) { +>f1 : Symbol(f1, Decl(typeGuardIntersectionTypes.ts, 15, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + if (isX(obj) || isY(obj) || isZ(obj)) { +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + + obj; +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + } + if (isX(obj) && isY(obj) && isZ(obj)) { +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + + obj; +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + } +} + +// Repro from #8911 + +// two interfaces +interface A { +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) + + a: string; +>a : Symbol(A.a, Decl(typeGuardIntersectionTypes.ts, 29, 13)) +} + +interface B { +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + b: string; +>b : Symbol(B.b, Decl(typeGuardIntersectionTypes.ts, 33, 13)) +} + +// a type guard for B +function isB(toTest: any): toTest is B { +>isB : Symbol(isB, Decl(typeGuardIntersectionTypes.ts, 35, 1)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + return toTest && toTest.b; +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { +>union : Symbol(union, Decl(typeGuardIntersectionTypes.ts, 40, 1)) +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + if (isB(a)) { +>isB : Symbol(isB, Decl(typeGuardIntersectionTypes.ts, 35, 1)) +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) + + return a; +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) + + } else { + return null; + } +} + +// Repro from #9011 + +declare function log(s: string): void; +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>s : Symbol(s, Decl(typeGuardIntersectionTypes.ts, 53, 21)) + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) + +interface Legged { legs: number; } +>Legged : Symbol(Legged, Decl(typeGuardIntersectionTypes.ts, 56, 54)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + +interface Winged { wings: boolean; } +>Winged : Symbol(Winged, Decl(typeGuardIntersectionTypes.ts, 57, 37)) +>wings : Symbol(Winged.wings, Decl(typeGuardIntersectionTypes.ts, 58, 21)) + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>Legged : Symbol(Legged, Decl(typeGuardIntersectionTypes.ts, 56, 54)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>x.legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) + +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>Winged : Symbol(Winged, Decl(typeGuardIntersectionTypes.ts, 57, 37)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>x.wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { +>identifyBeast : Symbol(identifyBeast, Decl(typeGuardIntersectionTypes.ts, 62, 67)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) + + // All beasts with legs + if (hasLegs(beast)) { +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + // All winged beasts with legs + if (hasWings(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + + log(`pegasus - 4 legs, wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else if (beast.legs === 2) { // ERROR TS2339... +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + + log(`bird - 2 legs, wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else { + log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + log(`quetzalcoatl - no legs, wings`) +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else { + log(`snake - no legs, no wings`) +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + } +} + +function beastFoo(beast: Object) { +>beastFoo : Symbol(beastFoo, Decl(typeGuardIntersectionTypes.ts, 98, 1)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + if (hasWings(beast) && hasLegs(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + beast // beast is Legged +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + // ideally, beast would be Winged && Legged here... + } + else { + beast +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + } + + if (hasLegs(beast) && hasWings(beast)) { +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + beast // beast is Winged +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + // ideally, beast would be Legged && Winged here... + } +} diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types new file mode 100644 index 00000000000..4018036c5ce --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.types @@ -0,0 +1,310 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts === + +interface X { +>X : X + + x: string; +>x : string +} + +interface Y { +>Y : Y + + y: string; +>y : string +} + +interface Z { +>Z : Z + + z: string; +>z : string +} + +declare function isX(obj: any): obj is X; +>isX : (obj: any) => obj is X +>obj : any +>obj : any +>X : X + +declare function isY(obj: any): obj is Y; +>isY : (obj: any) => obj is Y +>obj : any +>obj : any +>Y : Y + +declare function isZ(obj: any): obj is Z; +>isZ : (obj: any) => obj is Z +>obj : any +>obj : any +>Z : Z + +function f1(obj: Object) { +>f1 : (obj: Object) => void +>obj : Object +>Object : Object + + if (isX(obj) || isY(obj) || isZ(obj)) { +>isX(obj) || isY(obj) || isZ(obj) : boolean +>isX(obj) || isY(obj) : boolean +>isX(obj) : boolean +>isX : (obj: any) => obj is X +>obj : Object +>isY(obj) : boolean +>isY : (obj: any) => obj is Y +>obj : Object +>isZ(obj) : boolean +>isZ : (obj: any) => obj is Z +>obj : Object + + obj; +>obj : X | Y | Z + } + if (isX(obj) && isY(obj) && isZ(obj)) { +>isX(obj) && isY(obj) && isZ(obj) : boolean +>isX(obj) && isY(obj) : boolean +>isX(obj) : boolean +>isX : (obj: any) => obj is X +>obj : Object +>isY(obj) : boolean +>isY : (obj: any) => obj is Y +>obj : X +>isZ(obj) : boolean +>isZ : (obj: any) => obj is Z +>obj : X & Y + + obj; +>obj : X & Y & Z + } +} + +// Repro from #8911 + +// two interfaces +interface A { +>A : A + + a: string; +>a : string +} + +interface B { +>B : B + + b: string; +>b : string +} + +// a type guard for B +function isB(toTest: any): toTest is B { +>isB : (toTest: any) => toTest is B +>toTest : any +>toTest : any +>B : B + + return toTest && toTest.b; +>toTest && toTest.b : any +>toTest : any +>toTest.b : any +>toTest : any +>b : any +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { +>union : (a: A) => (A & B) | null +>a : A +>A : A +>A : A +>B : B +>null : null + + if (isB(a)) { +>isB(a) : boolean +>isB : (toTest: any) => toTest is B +>a : A + + return a; +>a : A & B + + } else { + return null; +>null : null + } +} + +// Repro from #9011 + +declare function log(s: string): void; +>log : (s: string) => void +>s : string + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +>Beast : Beast +>wings : boolean | undefined +>legs : number | undefined + +interface Legged { legs: number; } +>Legged : Legged +>legs : number + +interface Winged { wings: boolean; } +>Winged : Winged +>wings : boolean + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +>hasLegs : (x: Beast) => x is Legged +>x : Beast +>Beast : Beast +>x : any +>Legged : Legged +>x && typeof x.legs === 'number' : boolean +>x : Beast +>typeof x.legs === 'number' : boolean +>typeof x.legs : string +>x.legs : number | undefined +>x : Beast +>legs : number | undefined +>'number' : string + +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } +>hasWings : (x: Beast) => x is Winged +>x : Beast +>Beast : Beast +>x : any +>Winged : Winged +>x && !!x.wings : boolean +>x : Beast +>!!x.wings : boolean +>!x.wings : boolean +>x.wings : boolean | undefined +>x : Beast +>wings : boolean | undefined + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { +>identifyBeast : (beast: Beast) => void +>beast : Beast +>Beast : Beast + + // All beasts with legs + if (hasLegs(beast)) { +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Beast + + // All winged beasts with legs + if (hasWings(beast)) { +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Legged + + if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. +>beast.legs === 4 : boolean +>beast.legs : number +>beast : Legged & Winged +>legs : number +>4 : number + + log(`pegasus - 4 legs, wings`); +>log(`pegasus - 4 legs, wings`) : void +>log : (s: string) => void +>`pegasus - 4 legs, wings` : string + } + else if (beast.legs === 2) { // ERROR TS2339... +>beast.legs === 2 : boolean +>beast.legs : number +>beast : Legged & Winged +>legs : number +>2 : number + + log(`bird - 2 legs, wings`); +>log(`bird - 2 legs, wings`) : void +>log : (s: string) => void +>`bird - 2 legs, wings` : string + } + else { + log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... +>log(`unknown - ${beast.legs} legs, wings`) : void +>log : (s: string) => void +>`unknown - ${beast.legs} legs, wings` : string +>beast.legs : number +>beast : Legged & Winged +>legs : number + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); +>log(`manbearpig - ${beast.legs} legs, no wings`) : void +>log : (s: string) => void +>`manbearpig - ${beast.legs} legs, no wings` : string +>beast.legs : number +>beast : Legged +>legs : number + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Beast + + log(`quetzalcoatl - no legs, wings`) +>log(`quetzalcoatl - no legs, wings`) : void +>log : (s: string) => void +>`quetzalcoatl - no legs, wings` : string + } + else { + log(`snake - no legs, no wings`) +>log(`snake - no legs, no wings`) : void +>log : (s: string) => void +>`snake - no legs, no wings` : string + } + } +} + +function beastFoo(beast: Object) { +>beastFoo : (beast: Object) => void +>beast : Object +>Object : Object + + if (hasWings(beast) && hasLegs(beast)) { +>hasWings(beast) && hasLegs(beast) : boolean +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Object +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Winged + + beast // beast is Legged +>beast : Winged & Legged + + // ideally, beast would be Winged && Legged here... + } + else { + beast +>beast : Object + } + + if (hasLegs(beast) && hasWings(beast)) { +>hasLegs(beast) && hasWings(beast) : boolean +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Object +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Legged + + beast // beast is Winged +>beast : Legged & Winged + + // ideally, beast would be Legged && Winged here... + } +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts new file mode 100644 index 00000000000..efde587e5e5 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts @@ -0,0 +1,115 @@ +// @strictNullChecks: true + +interface X { + x: string; +} + +interface Y { + y: string; +} + +interface Z { + z: string; +} + +declare function isX(obj: any): obj is X; +declare function isY(obj: any): obj is Y; +declare function isZ(obj: any): obj is Z; + +function f1(obj: Object) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} + +// Repro from #8911 + +// two interfaces +interface A { + a: string; +} + +interface B { + b: string; +} + +// a type guard for B +function isB(toTest: any): toTest is B { + return toTest && toTest.b; +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { + if (isB(a)) { + return a; + } else { + return null; + } +} + +// Repro from #9011 + +declare function log(s: string): void; + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +interface Legged { legs: number; } +interface Winged { wings: boolean; } + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { + + // All beasts with legs + if (hasLegs(beast)) { + + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. + log(`pegasus - 4 legs, wings`); + } + else if (beast.legs === 2) { // ERROR TS2339... + log(`bird - 2 legs, wings`); + } + else { + log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { + log(`quetzalcoatl - no legs, wings`) + } + else { + log(`snake - no legs, no wings`) + } + } +} + +function beastFoo(beast: Object) { + if (hasWings(beast) && hasLegs(beast)) { + beast // beast is Legged + // ideally, beast would be Winged && Legged here... + } + else { + beast + } + + if (hasLegs(beast) && hasWings(beast)) { + beast // beast is Winged + // ideally, beast would be Legged && Winged here... + } +} \ No newline at end of file