1// @strict: true 2 3type Item = Item1 | Item2; 4 5interface Base { 6 bar: boolean; 7} 8 9interface Item1 extends Base { 10 kind: "A"; 11 foo: string | undefined; 12 baz: boolean; 13 qux: true; 14} 15 16interface Item2 extends Base { 17 kind: "B"; 18 foo: string | undefined; 19 baz: boolean; 20 qux: false; 21} 22 23function goo1(x: Item) { 24 if (x.kind === "A" && x.foo !== undefined) { 25 x.foo.length; 26 } 27} 28 29function goo2(x: Item) { 30 if (x.foo !== undefined && x.kind === "A") { 31 x.foo.length; // Error, intervening discriminant guard 32 } 33} 34 35function foo1(x: Item) { 36 if (x.bar && x.foo !== undefined) { 37 x.foo.length; 38 } 39} 40 41function foo2(x: Item) { 42 if (x.foo !== undefined && x.bar) { 43 x.foo.length; 44 } 45} 46 47function foo3(x: Item) { 48 if (x.baz && x.foo !== undefined) { 49 x.foo.length; 50 } 51} 52 53function foo4(x: Item) { 54 if (x.foo !== undefined && x.baz) { 55 x.foo.length; 56 } 57} 58 59function foo5(x: Item) { 60 if (x.qux && x.foo !== undefined) { 61 x.foo.length; 62 } 63} 64 65function foo6(x: Item) { 66 if (x.foo !== undefined && x.qux) { 67 x.foo.length; // Error, intervening discriminant guard 68 } 69} 70 71// Repro from #27493 72 73enum Types { Str = 1, Num = 2 } 74 75type Instance = StrType | NumType; 76 77interface StrType { 78 type: Types.Str; 79 value: string; 80 length: number; 81} 82 83interface NumType { 84 type: Types.Num; 85 value: number; 86} 87 88function func2(inst: Instance) { 89 while (true) { 90 switch (inst.type) { 91 case Types.Str: { 92 inst.value.length; 93 break; 94 } 95 case Types.Num: { 96 inst.value.toExponential; 97 break; 98 } 99 } 100 } 101} 102 103// Repro from #29106 104 105const f = (_a: string, _b: string): void => {}; 106 107interface A { 108 a?: string; 109 b?: string; 110} 111 112interface B { 113 a: string; 114 b: string; 115} 116 117type U = A | B; 118 119const u: U = {} as any; 120 121u.a && u.b && f(u.a, u.b); 122 123u.b && u.a && f(u.a, u.b); 124 125// Repro from #29012 126 127type Additive = '+' | '-'; 128type Multiplicative = '*' | '/'; 129 130interface AdditiveObj { 131 key: Additive 132} 133 134interface MultiplicativeObj { 135 key: Multiplicative 136} 137 138type Obj = AdditiveObj | MultiplicativeObj 139 140export function foo(obj: Obj) { 141 switch (obj.key) { 142 case '+': { 143 onlyPlus(obj.key); 144 return; 145 } 146 } 147} 148 149function onlyPlus(arg: '+') { 150 return arg; 151} 152 153// Repro from #29496 154 155declare function never(value: never): never; 156 157const enum BarEnum { 158 bar1 = 1, 159 bar2 = 2, 160} 161 162type UnionOfBar = TypeBar1 | TypeBar2; 163type TypeBar1 = { type: BarEnum.bar1 }; 164type TypeBar2 = { type: BarEnum.bar2 }; 165 166function func3(value: Partial<UnionOfBar>) { 167 if (value.type !== undefined) { 168 switch (value.type) { 169 case BarEnum.bar1: 170 break; 171 case BarEnum.bar2: 172 break; 173 default: 174 never(value.type); 175 } 176 } 177} 178 179// Repro from #30557 180 181interface TypeA { 182 Name: "TypeA"; 183 Value1: "Cool stuff!"; 184} 185 186interface TypeB { 187 Name: "TypeB"; 188 Value2: 0; 189} 190 191type Type = TypeA | TypeB; 192 193declare function isType(x: unknown): x is Type; 194 195function WorksProperly(data: Type) { 196 if (data.Name === "TypeA") { 197 const value1 = data.Value1; 198 } 199} 200 201function DoesNotWork(data: unknown) { 202 if (isType(data)) { 203 if (data.Name === "TypeA") { 204 const value1 = data.Value1; 205 } 206 } 207} 208 209// Repro from #36777 210 211type TestA = { 212 type: 'testA'; 213 bananas: 3; 214} 215 216type TestB = { 217 type: 'testB'; 218 apples: 5; 219} 220 221type AllTests = TestA | TestB; 222 223type MapOfAllTests = Record<string, AllTests>; 224 225const doTestingStuff = (mapOfTests: MapOfAllTests, ids: string[]) => { 226 ids.forEach(id => { 227 let test; 228 test = mapOfTests[id]; 229 if (test.type === 'testA') { 230 console.log(test.bananas); 231 } 232 switch (test.type) { 233 case 'testA': { 234 console.log(test.bananas); 235 } 236 } 237 }); 238}; 239