From a5ea55ab6561b932a57d18593f89c81ad1d0f9f0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 9 Aug 2016 13:39:00 -0700 Subject: [PATCH 1/2] Ignore null, undefined, void when checking for discriminant property --- src/compiler/checker.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f2eaea833..c88ab16acd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7837,14 +7837,17 @@ namespace ts { } function isDiscriminantProperty(type: Type, name: string) { - if (type && type.flags & TypeFlags.Union) { - const prop = getPropertyOfType(type, name); - if (prop && prop.flags & SymbolFlags.SyntheticProperty) { - if ((prop).isDiscriminantProperty === undefined) { - (prop).isDiscriminantProperty = !(prop).hasCommonType && - isUnitUnionType(getTypeOfSymbol(prop)); + if (type) { + const nonNullType = getNonNullableType(type); + if (nonNullType.flags & TypeFlags.Union) { + const prop = getPropertyOfType(nonNullType, name); + if (prop && prop.flags & SymbolFlags.SyntheticProperty) { + if ((prop).isDiscriminantProperty === undefined) { + (prop).isDiscriminantProperty = !(prop).hasCommonType && + isUnitUnionType(getTypeOfSymbol(prop)); + } + return (prop).isDiscriminantProperty; } - return (prop).isDiscriminantProperty; } } return false; From 6c0bca0ae583c6b68ae9e007c081338ce9047d51 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 9 Aug 2016 13:39:12 -0700 Subject: [PATCH 2/2] Add regression test --- .../discriminantsAndNullOrUndefined.js | 44 ++++++++++++ .../discriminantsAndNullOrUndefined.symbols | 61 +++++++++++++++++ .../discriminantsAndNullOrUndefined.types | 68 +++++++++++++++++++ .../discriminantsAndNullOrUndefined.ts | 25 +++++++ 4 files changed, 198 insertions(+) create mode 100644 tests/baselines/reference/discriminantsAndNullOrUndefined.js create mode 100644 tests/baselines/reference/discriminantsAndNullOrUndefined.symbols create mode 100644 tests/baselines/reference/discriminantsAndNullOrUndefined.types create mode 100644 tests/cases/compiler/discriminantsAndNullOrUndefined.ts diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.js b/tests/baselines/reference/discriminantsAndNullOrUndefined.js new file mode 100644 index 0000000000..153950f8ab --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.js @@ -0,0 +1,44 @@ +//// [discriminantsAndNullOrUndefined.ts] + +// Repro from #10228 + +interface A { kind: 'A'; } +interface B { kind: 'B'; } + +type C = A | B | undefined; + +function never(_: never): never { + throw new Error(); +} + +function useA(_: A): void { } +function useB(_: B): void { } + +declare var c: C; + +if (c !== undefined) { + switch (c.kind) { + case 'A': useA(c); break; + case 'B': useB(c); break; + default: never(c); + } +} + +//// [discriminantsAndNullOrUndefined.js] +// Repro from #10228 +function never(_) { + throw new Error(); +} +function useA(_) { } +function useB(_) { } +if (c !== undefined) { + switch (c.kind) { + case 'A': + useA(c); + break; + case 'B': + useB(c); + break; + default: never(c); + } +} diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols b/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols new file mode 100644 index 0000000000..3f95fcf3a3 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols @@ -0,0 +1,61 @@ +=== tests/cases/compiler/discriminantsAndNullOrUndefined.ts === + +// Repro from #10228 + +interface A { kind: 'A'; } +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) +>kind : Symbol(A.kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13)) + +interface B { kind: 'B'; } +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) +>kind : Symbol(B.kind, Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) + +type C = A | B | undefined; +>C : Symbol(C, Decl(discriminantsAndNullOrUndefined.ts, 4, 26)) +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) + +function never(_: never): never { +>never : Symbol(never, Decl(discriminantsAndNullOrUndefined.ts, 6, 27)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 8, 15)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +} + +function useA(_: A): void { } +>useA : Symbol(useA, Decl(discriminantsAndNullOrUndefined.ts, 10, 1)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 12, 14)) +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) + +function useB(_: B): void { } +>useB : Symbol(useB, Decl(discriminantsAndNullOrUndefined.ts, 12, 29)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 13, 14)) +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) + +declare var c: C; +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>C : Symbol(C, Decl(discriminantsAndNullOrUndefined.ts, 4, 26)) + +if (c !== undefined) { +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>undefined : Symbol(undefined) + + switch (c.kind) { +>c.kind : Symbol(kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13), Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>kind : Symbol(kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13), Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) + + case 'A': useA(c); break; +>useA : Symbol(useA, Decl(discriminantsAndNullOrUndefined.ts, 10, 1)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + + case 'B': useB(c); break; +>useB : Symbol(useB, Decl(discriminantsAndNullOrUndefined.ts, 12, 29)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + + default: never(c); +>never : Symbol(never, Decl(discriminantsAndNullOrUndefined.ts, 6, 27)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + } +} diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.types b/tests/baselines/reference/discriminantsAndNullOrUndefined.types new file mode 100644 index 0000000000..7a2918ab83 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.types @@ -0,0 +1,68 @@ +=== tests/cases/compiler/discriminantsAndNullOrUndefined.ts === + +// Repro from #10228 + +interface A { kind: 'A'; } +>A : A +>kind : "A" + +interface B { kind: 'B'; } +>B : B +>kind : "B" + +type C = A | B | undefined; +>C : C +>A : A +>B : B + +function never(_: never): never { +>never : (_: never) => never +>_ : never + + throw new Error(); +>new Error() : Error +>Error : ErrorConstructor +} + +function useA(_: A): void { } +>useA : (_: A) => void +>_ : A +>A : A + +function useB(_: B): void { } +>useB : (_: B) => void +>_ : B +>B : B + +declare var c: C; +>c : C +>C : C + +if (c !== undefined) { +>c !== undefined : boolean +>c : C +>undefined : undefined + + switch (c.kind) { +>c.kind : "A" | "B" +>c : A | B +>kind : "A" | "B" + + case 'A': useA(c); break; +>'A' : "A" +>useA(c) : void +>useA : (_: A) => void +>c : A + + case 'B': useB(c); break; +>'B' : "B" +>useB(c) : void +>useB : (_: B) => void +>c : B + + default: never(c); +>never(c) : never +>never : (_: never) => never +>c : never + } +} diff --git a/tests/cases/compiler/discriminantsAndNullOrUndefined.ts b/tests/cases/compiler/discriminantsAndNullOrUndefined.ts new file mode 100644 index 0000000000..8346fa8ade --- /dev/null +++ b/tests/cases/compiler/discriminantsAndNullOrUndefined.ts @@ -0,0 +1,25 @@ +// @strictNullChecks: true + +// Repro from #10228 + +interface A { kind: 'A'; } +interface B { kind: 'B'; } + +type C = A | B | undefined; + +function never(_: never): never { + throw new Error(); +} + +function useA(_: A): void { } +function useB(_: B): void { } + +declare var c: C; + +if (c !== undefined) { + switch (c.kind) { + case 'A': useA(c); break; + case 'B': useB(c); break; + default: never(c); + } +} \ No newline at end of file