From 267e5989cd7fdd3b6a34661e269c1c8634b835d9 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Thu, 6 Sep 2018 16:56:31 +0100 Subject: [PATCH 1/5] Add narrowing for unknown with strict equal --- src/compiler/checker.ts | 3 + tests/baselines/reference/unknownType2.js | 82 ++++++++++ .../baselines/reference/unknownType2.symbols | 124 +++++++++++++++ tests/baselines/reference/unknownType2.types | 148 ++++++++++++++++++ .../conformance/types/unknown/unknownType2.ts | 51 ++++++ 5 files changed, 408 insertions(+) create mode 100644 tests/baselines/reference/unknownType2.js create mode 100644 tests/baselines/reference/unknownType2.symbols create mode 100644 tests/baselines/reference/unknownType2.types create mode 100644 tests/cases/conformance/types/unknown/unknownType2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8947f98f8647b..96d6437e62e77 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16018,6 +16018,9 @@ namespace ts { assumeTrue = !assumeTrue; } const valueType = getTypeOfExpression(value); + if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && (valueType.flags & TypeFlags.Unit)) { + return assumeTrue ? valueType : type; + } if (valueType.flags & TypeFlags.Nullable) { if (!strictNullChecks) { return type; diff --git a/tests/baselines/reference/unknownType2.js b/tests/baselines/reference/unknownType2.js new file mode 100644 index 0000000000000..59a63b1bd9463 --- /dev/null +++ b/tests/baselines/reference/unknownType2.js @@ -0,0 +1,82 @@ +//// [unknownType2.ts] +type isUnknown = unknown extends T ? true : false; +type isTrue = T; + +type SomeResponse = 'yes' | 'no' | 'idk'; +let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error + +const u: unknown = undefined; + +declare const symb: unique symbol; + +if (u === 5) { + const y = u.toString(10); +} + +if (u === true || u === false) { + const someBool: boolean = u; +} + +if (u === undefined) { + const undef: undefined = u; +} + +if (u === null) { + const someNull: null = u; +} + +if(u === symb) { + const symbolAlias: typeof symb = u; +} + +if (!(u === 42)) { + u // u should still be `unknown` here +} + +if (u !== 42) { + type A = isTrue> +} + +if (u == 42) { + type B = isTrue> +} + +if (u == true) { + type C = isTrue> +} + +if (u == Object) { + type D = isTrue> +} + + +//// [unknownType2.js] +"use strict"; +var validate = function (x) { return (x === 'yes' || x === 'no') ? x : 'idk'; }; // No error +var u = undefined; +if (u === 5) { + var y = u.toString(10); +} +if (u === true || u === false) { + var someBool = u; +} +if (u === undefined) { + var undef = u; +} +if (u === null) { + var someNull = u; +} +if (u === symb) { + var symbolAlias = u; +} +if (!(u === 42)) { + u; // u should still be `unknown` here +} +if (u !== 42) { +} +if (u == 42) { +} +if (u == true) { +} +if (u == Object) { +} diff --git a/tests/baselines/reference/unknownType2.symbols b/tests/baselines/reference/unknownType2.symbols new file mode 100644 index 0000000000000..0cd1ded5d832c --- /dev/null +++ b/tests/baselines/reference/unknownType2.symbols @@ -0,0 +1,124 @@ +=== tests/cases/conformance/types/unknown/unknownType2.ts === +type isUnknown = unknown extends T ? true : false; +>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) +>T : Symbol(T, Decl(unknownType2.ts, 0, 15)) +>T : Symbol(T, Decl(unknownType2.ts, 0, 15)) + +type isTrue = T; +>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) +>T : Symbol(T, Decl(unknownType2.ts, 1, 12)) +>T : Symbol(T, Decl(unknownType2.ts, 1, 12)) + +type SomeResponse = 'yes' | 'no' | 'idk'; +>SomeResponse : Symbol(SomeResponse, Decl(unknownType2.ts, 1, 32)) + +let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error +>validate : Symbol(validate, Decl(unknownType2.ts, 4, 3)) +>x : Symbol(x, Decl(unknownType2.ts, 4, 15)) +>SomeResponse : Symbol(SomeResponse, Decl(unknownType2.ts, 1, 32)) +>x : Symbol(x, Decl(unknownType2.ts, 4, 44)) +>x : Symbol(x, Decl(unknownType2.ts, 4, 44)) +>x : Symbol(x, Decl(unknownType2.ts, 4, 44)) +>x : Symbol(x, Decl(unknownType2.ts, 4, 44)) + +const u: unknown = undefined; +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>undefined : Symbol(undefined) + +declare const symb: unique symbol; +>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13)) + +if (u === 5) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) + + const y = u.toString(10); +>y : Symbol(y, Decl(unknownType2.ts, 11, 9)) +>u.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +} + +if (u === true || u === false) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) + + const someBool: boolean = u; +>someBool : Symbol(someBool, Decl(unknownType2.ts, 15, 9)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === undefined) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>undefined : Symbol(undefined) + + const undef: undefined = u; +>undef : Symbol(undef, Decl(unknownType2.ts, 19, 9)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === null) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) + + const someNull: null = u; +>someNull : Symbol(someNull, Decl(unknownType2.ts, 23, 9)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if(u === symb) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13)) + + const symbolAlias: typeof symb = u; +>symbolAlias : Symbol(symbolAlias, Decl(unknownType2.ts, 27, 9)) +>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (!(u === 42)) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) + + u // u should still be `unknown` here +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u !== 42) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) + + type A = isTrue> +>A : Symbol(A, Decl(unknownType2.ts, 34, 15)) +>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) +>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u == 42) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) + + type B = isTrue> +>B : Symbol(B, Decl(unknownType2.ts, 38, 14)) +>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) +>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u == true) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) + + type C = isTrue> +>C : Symbol(C, Decl(unknownType2.ts, 42, 16)) +>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) +>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u == Object) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + type D = isTrue> +>D : Symbol(D, Decl(unknownType2.ts, 46, 18)) +>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) +>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + diff --git a/tests/baselines/reference/unknownType2.types b/tests/baselines/reference/unknownType2.types new file mode 100644 index 0000000000000..41743b0c49947 --- /dev/null +++ b/tests/baselines/reference/unknownType2.types @@ -0,0 +1,148 @@ +=== tests/cases/conformance/types/unknown/unknownType2.ts === +type isUnknown = unknown extends T ? true : false; +>isUnknown : isUnknown +>true : true +>false : false + +type isTrue = T; +>isTrue : T +>true : true + +type SomeResponse = 'yes' | 'no' | 'idk'; +>SomeResponse : SomeResponse + +let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error +>validate : (x: unknown) => SomeResponse +>x : unknown +>x => (x === 'yes' || x === 'no') ? x : 'idk' : (x: unknown) => "yes" | "no" | "idk" +>x : unknown +>(x === 'yes' || x === 'no') ? x : 'idk' : SomeResponse +>(x === 'yes' || x === 'no') : boolean +>x === 'yes' || x === 'no' : boolean +>x === 'yes' : boolean +>x : unknown +>'yes' : "yes" +>x === 'no' : boolean +>x : unknown +>'no' : "no" +>x : "yes" | "no" +>'idk' : "idk" + +const u: unknown = undefined; +>u : unknown +>undefined : undefined + +declare const symb: unique symbol; +>symb : unique symbol + +if (u === 5) { +>u === 5 : boolean +>u : unknown +>5 : 5 + + const y = u.toString(10); +>y : string +>u.toString(10) : string +>u.toString : (radix?: number | undefined) => string +>u : 5 +>toString : (radix?: number | undefined) => string +>10 : 10 +} + +if (u === true || u === false) { +>u === true || u === false : boolean +>u === true : boolean +>u : unknown +>true : true +>u === false : boolean +>u : unknown +>false : false + + const someBool: boolean = u; +>someBool : boolean +>u : boolean +} + +if (u === undefined) { +>u === undefined : boolean +>u : unknown +>undefined : undefined + + const undef: undefined = u; +>undef : undefined +>u : undefined +} + +if (u === null) { +>u === null : boolean +>u : unknown +>null : null + + const someNull: null = u; +>someNull : null +>null : null +>u : null +} + +if(u === symb) { +>u === symb : boolean +>u : unknown +>symb : unique symbol + + const symbolAlias: typeof symb = u; +>symbolAlias : unique symbol +>symb : unique symbol +>u : unique symbol +} + +if (!(u === 42)) { +>!(u === 42) : boolean +>(u === 42) : boolean +>u === 42 : boolean +>u : unknown +>42 : 42 + + u // u should still be `unknown` here +>u : unknown +} + +if (u !== 42) { +>u !== 42 : boolean +>u : unknown +>42 : 42 + + type A = isTrue> +>A : true +>u : unknown +} + +if (u == 42) { +>u == 42 : boolean +>u : unknown +>42 : 42 + + type B = isTrue> +>B : true +>u : unknown +} + +if (u == true) { +>u == true : boolean +>u : unknown +>true : true + + type C = isTrue> +>C : true +>u : unknown +} + +if (u == Object) { +>u == Object : boolean +>u : unknown +>Object : ObjectConstructor + + type D = isTrue> +>D : true +>u : unknown +} + diff --git a/tests/cases/conformance/types/unknown/unknownType2.ts b/tests/cases/conformance/types/unknown/unknownType2.ts new file mode 100644 index 0000000000000..b644700044c64 --- /dev/null +++ b/tests/cases/conformance/types/unknown/unknownType2.ts @@ -0,0 +1,51 @@ +// @strict: true + +type isUnknown = unknown extends T ? true : false; +type isTrue = T; + +type SomeResponse = 'yes' | 'no' | 'idk'; +let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error + +const u: unknown = undefined; + +declare const symb: unique symbol; + +if (u === 5) { + const y = u.toString(10); +} + +if (u === true || u === false) { + const someBool: boolean = u; +} + +if (u === undefined) { + const undef: undefined = u; +} + +if (u === null) { + const someNull: null = u; +} + +if(u === symb) { + const symbolAlias: typeof symb = u; +} + +if (!(u === 42)) { + u // u should still be `unknown` here +} + +if (u !== 42) { + type A = isTrue> +} + +if (u == 42) { + type B = isTrue> +} + +if (u == true) { + type C = isTrue> +} + +if (u == Object) { + type D = isTrue> +} From 1f2338bd786d9b0b418ab45075af90fe86a6145a Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Thu, 13 Sep 2018 11:44:19 +0100 Subject: [PATCH 2/5] Update test file and baselines --- tests/baselines/reference/unknownType2.js | 16 ++++++------ .../baselines/reference/unknownType2.symbols | 26 +++++++++++-------- tests/baselines/reference/unknownType2.types | 24 +++++++++-------- .../conformance/types/unknown/unknownType2.ts | 15 ++++++----- 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/tests/baselines/reference/unknownType2.js b/tests/baselines/reference/unknownType2.js index 59a63b1bd9463..a1305e823b8a6 100644 --- a/tests/baselines/reference/unknownType2.js +++ b/tests/baselines/reference/unknownType2.js @@ -13,7 +13,7 @@ if (u === 5) { const y = u.toString(10); } -if (u === true || u === false) { +if (u === true || u === false) { const someBool: boolean = u; } @@ -25,28 +25,29 @@ if (u === null) { const someNull: null = u; } -if(u === symb) { +if (u === symb) { const symbolAlias: typeof symb = u; } if (!(u === 42)) { - u // u should still be `unknown` here + type A = isTrue> + } if (u !== 42) { - type A = isTrue> + type B = isTrue> } if (u == 42) { - type B = isTrue> + type C = isTrue> } if (u == true) { - type C = isTrue> + type D = isTrue> } if (u == Object) { - type D = isTrue> + type E = isTrue> } @@ -70,7 +71,6 @@ if (u === symb) { var symbolAlias = u; } if (!(u === 42)) { - u; // u should still be `unknown` here } if (u !== 42) { } diff --git a/tests/baselines/reference/unknownType2.symbols b/tests/baselines/reference/unknownType2.symbols index 0cd1ded5d832c..8c3f8a6e04afc 100644 --- a/tests/baselines/reference/unknownType2.symbols +++ b/tests/baselines/reference/unknownType2.symbols @@ -38,7 +38,7 @@ if (u === 5) { >toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) } -if (u === true || u === false) { +if (u === true || u === false) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) @@ -64,7 +64,7 @@ if (u === null) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) } -if(u === symb) { +if (u === symb) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) >symb : Symbol(symb, Decl(unknownType2.ts, 8, 13)) @@ -77,15 +77,19 @@ if(u === symb) { if (!(u === 42)) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) - u // u should still be `unknown` here + type A = isTrue> +>A : Symbol(A, Decl(unknownType2.ts, 30, 18)) +>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) +>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) + } if (u !== 42) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) - type A = isTrue> ->A : Symbol(A, Decl(unknownType2.ts, 34, 15)) + type B = isTrue> +>B : Symbol(B, Decl(unknownType2.ts, 35, 15)) >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) @@ -94,8 +98,8 @@ if (u !== 42) { if (u == 42) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) - type B = isTrue> ->B : Symbol(B, Decl(unknownType2.ts, 38, 14)) + type C = isTrue> +>C : Symbol(C, Decl(unknownType2.ts, 39, 14)) >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) @@ -104,8 +108,8 @@ if (u == 42) { if (u == true) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) - type C = isTrue> ->C : Symbol(C, Decl(unknownType2.ts, 42, 16)) + type D = isTrue> +>D : Symbol(D, Decl(unknownType2.ts, 43, 16)) >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) @@ -115,8 +119,8 @@ if (u == Object) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) - type D = isTrue> ->D : Symbol(D, Decl(unknownType2.ts, 46, 18)) + type E = isTrue> +>E : Symbol(E, Decl(unknownType2.ts, 47, 18)) >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) diff --git a/tests/baselines/reference/unknownType2.types b/tests/baselines/reference/unknownType2.types index 41743b0c49947..6f9195b79946a 100644 --- a/tests/baselines/reference/unknownType2.types +++ b/tests/baselines/reference/unknownType2.types @@ -49,7 +49,7 @@ if (u === 5) { >10 : 10 } -if (u === true || u === false) { +if (u === true || u === false) { >u === true || u === false : boolean >u === true : boolean >u : unknown @@ -84,7 +84,7 @@ if (u === null) { >u : null } -if(u === symb) { +if (u === symb) { >u === symb : boolean >u : unknown >symb : unique symbol @@ -102,8 +102,10 @@ if (!(u === 42)) { >u : unknown >42 : 42 - u // u should still be `unknown` here + type A = isTrue> +>A : true >u : unknown + } if (u !== 42) { @@ -111,8 +113,8 @@ if (u !== 42) { >u : unknown >42 : 42 - type A = isTrue> ->A : true + type B = isTrue> +>B : true >u : unknown } @@ -121,8 +123,8 @@ if (u == 42) { >u : unknown >42 : 42 - type B = isTrue> ->B : true + type C = isTrue> +>C : true >u : unknown } @@ -131,8 +133,8 @@ if (u == true) { >u : unknown >true : true - type C = isTrue> ->C : true + type D = isTrue> +>D : true >u : unknown } @@ -141,8 +143,8 @@ if (u == Object) { >u : unknown >Object : ObjectConstructor - type D = isTrue> ->D : true + type E = isTrue> +>E : true >u : unknown } diff --git a/tests/cases/conformance/types/unknown/unknownType2.ts b/tests/cases/conformance/types/unknown/unknownType2.ts index b644700044c64..c0da9a4e782b3 100644 --- a/tests/cases/conformance/types/unknown/unknownType2.ts +++ b/tests/cases/conformance/types/unknown/unknownType2.ts @@ -14,7 +14,7 @@ if (u === 5) { const y = u.toString(10); } -if (u === true || u === false) { +if (u === true || u === false) { const someBool: boolean = u; } @@ -26,26 +26,27 @@ if (u === null) { const someNull: null = u; } -if(u === symb) { +if (u === symb) { const symbolAlias: typeof symb = u; } if (!(u === 42)) { - u // u should still be `unknown` here + type A = isTrue> + } if (u !== 42) { - type A = isTrue> + type B = isTrue> } if (u == 42) { - type B = isTrue> + type C = isTrue> } if (u == true) { - type C = isTrue> + type D = isTrue> } if (u == Object) { - type D = isTrue> + type E = isTrue> } From 36cc154985992db89214f1c51165b24c36855226 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Thu, 13 Sep 2018 19:28:40 +0100 Subject: [PATCH 3/5] Narrow to any primitive or object and update tests --- src/compiler/checker.ts | 10 +- tests/baselines/reference/unknownType2.js | 109 ++++++++++- .../baselines/reference/unknownType2.symbols | 177 ++++++++++++++++- tests/baselines/reference/unknownType2.types | 179 +++++++++++++++++- .../compiler/strictNullEmptyDestructuring.ts~ | 23 +++ .../conformance/types/unknown/unknownType2.ts | 66 ++++++- 6 files changed, 554 insertions(+), 10 deletions(-) create mode 100644 tests/cases/compiler/strictNullEmptyDestructuring.ts~ diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 96d6437e62e77..29be16bd21f09 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16018,8 +16018,14 @@ namespace ts { assumeTrue = !assumeTrue; } const valueType = getTypeOfExpression(value); - if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && (valueType.flags & TypeFlags.Unit)) { - return assumeTrue ? valueType : type; + if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && assumeTrue) { + if (valueType.flags & TypeFlags.Primitive || valueType.flags & TypeFlags.NonPrimitive) { + return valueType; + } + if (valueType.flags & TypeFlags.Object) { + return nonPrimitiveType; + } + return type; } if (valueType.flags & TypeFlags.Nullable) { if (!strictNullChecks) { diff --git a/tests/baselines/reference/unknownType2.js b/tests/baselines/reference/unknownType2.js index a1305e823b8a6..66b763efb5808 100644 --- a/tests/baselines/reference/unknownType2.js +++ b/tests/baselines/reference/unknownType2.js @@ -31,7 +31,6 @@ if (u === symb) { if (!(u === 42)) { type A = isTrue> - } if (u !== 42) { @@ -49,6 +48,71 @@ if (u == true) { if (u == Object) { type E = isTrue> } + +declare const aString: string; +declare const aBoolean: boolean; +declare const aNumber: number; +declare const anObject: object; +declare const anObjectLiteral: { x: number }; +declare const aUnion: { x: number } | { y: string }; +declare const anIntersection: { x: number } & { y: string }; +declare const aFunction: () => number; + +if (u === aString) { + let uString: string = u; +} + +if (u === aBoolean) { + let uString: boolean = u; +} + +if (u === aNumber) { + let uNumber: number = u; +} + +if (u === anObject) { + let uObject: object = u; +} + +if (u === anObjectLiteral) { + let uObjectLiteral: object = u; +} + +if (u === aUnion) { + type unionDoesNotNarrow = isTrue> +} + +if (u === anIntersection) { + type intersectionDoesNotNarrow = isTrue> +} + +if (u === aFunction) { + let uFunction: object = u; +} + +enum NumberEnum { + A, + B, + C +} + +enum StringEnum { + A = "A", + B = "B", + C = "C" +} + +if (u === NumberEnum || u === StringEnum) { + let enumObj: object = u; +} + +if(u === NumberEnum.A) { + let a: NumberEnum.A = u +} + +if(u === StringEnum.B) { + let b: StringEnum.B = u +} //// [unknownType2.js] @@ -80,3 +144,46 @@ if (u == true) { } if (u == Object) { } +if (u === aString) { + var uString = u; +} +if (u === aBoolean) { + var uString = u; +} +if (u === aNumber) { + var uNumber = u; +} +if (u === anObject) { + var uObject = u; +} +if (u === anObjectLiteral) { + var uObjectLiteral = u; +} +if (u === aUnion) { +} +if (u === anIntersection) { +} +if (u === aFunction) { + var uFunction = u; +} +var NumberEnum; +(function (NumberEnum) { + NumberEnum[NumberEnum["A"] = 0] = "A"; + NumberEnum[NumberEnum["B"] = 1] = "B"; + NumberEnum[NumberEnum["C"] = 2] = "C"; +})(NumberEnum || (NumberEnum = {})); +var StringEnum; +(function (StringEnum) { + StringEnum["A"] = "A"; + StringEnum["B"] = "B"; + StringEnum["C"] = "C"; +})(StringEnum || (StringEnum = {})); +if (u === NumberEnum || u === StringEnum) { + var enumObj = u; +} +if (u === NumberEnum.A) { + var a = u; +} +if (u === StringEnum.B) { + var b = u; +} diff --git a/tests/baselines/reference/unknownType2.symbols b/tests/baselines/reference/unknownType2.symbols index 8c3f8a6e04afc..577b1186def8d 100644 --- a/tests/baselines/reference/unknownType2.symbols +++ b/tests/baselines/reference/unknownType2.symbols @@ -82,14 +82,13 @@ if (!(u === 42)) { >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) - } if (u !== 42) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) type B = isTrue> ->B : Symbol(B, Decl(unknownType2.ts, 35, 15)) +>B : Symbol(B, Decl(unknownType2.ts, 34, 15)) >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) @@ -99,7 +98,7 @@ if (u == 42) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) type C = isTrue> ->C : Symbol(C, Decl(unknownType2.ts, 39, 14)) +>C : Symbol(C, Decl(unknownType2.ts, 38, 14)) >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) @@ -109,7 +108,7 @@ if (u == true) { >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) type D = isTrue> ->D : Symbol(D, Decl(unknownType2.ts, 43, 16)) +>D : Symbol(D, Decl(unknownType2.ts, 42, 16)) >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) @@ -120,9 +119,177 @@ if (u == Object) { >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) type E = isTrue> ->E : Symbol(E, Decl(unknownType2.ts, 47, 18)) +>E : Symbol(E, Decl(unknownType2.ts, 46, 18)) >isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) >isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) >u : Symbol(u, Decl(unknownType2.ts, 6, 5)) } +declare const aString: string; +>aString : Symbol(aString, Decl(unknownType2.ts, 50, 13)) + +declare const aBoolean: boolean; +>aBoolean : Symbol(aBoolean, Decl(unknownType2.ts, 51, 13)) + +declare const aNumber: number; +>aNumber : Symbol(aNumber, Decl(unknownType2.ts, 52, 13)) + +declare const anObject: object; +>anObject : Symbol(anObject, Decl(unknownType2.ts, 53, 13)) + +declare const anObjectLiteral: { x: number }; +>anObjectLiteral : Symbol(anObjectLiteral, Decl(unknownType2.ts, 54, 13)) +>x : Symbol(x, Decl(unknownType2.ts, 54, 32)) + +declare const aUnion: { x: number } | { y: string }; +>aUnion : Symbol(aUnion, Decl(unknownType2.ts, 55, 13)) +>x : Symbol(x, Decl(unknownType2.ts, 55, 23)) +>y : Symbol(y, Decl(unknownType2.ts, 55, 39)) + +declare const anIntersection: { x: number } & { y: string }; +>anIntersection : Symbol(anIntersection, Decl(unknownType2.ts, 56, 13)) +>x : Symbol(x, Decl(unknownType2.ts, 56, 31)) +>y : Symbol(y, Decl(unknownType2.ts, 56, 47)) + +declare const aFunction: () => number; +>aFunction : Symbol(aFunction, Decl(unknownType2.ts, 57, 13)) + +if (u === aString) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>aString : Symbol(aString, Decl(unknownType2.ts, 50, 13)) + + let uString: string = u; +>uString : Symbol(uString, Decl(unknownType2.ts, 60, 7)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === aBoolean) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>aBoolean : Symbol(aBoolean, Decl(unknownType2.ts, 51, 13)) + + let uString: boolean = u; +>uString : Symbol(uString, Decl(unknownType2.ts, 64, 7)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === aNumber) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>aNumber : Symbol(aNumber, Decl(unknownType2.ts, 52, 13)) + + let uNumber: number = u; +>uNumber : Symbol(uNumber, Decl(unknownType2.ts, 68, 7)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === anObject) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>anObject : Symbol(anObject, Decl(unknownType2.ts, 53, 13)) + + let uObject: object = u; +>uObject : Symbol(uObject, Decl(unknownType2.ts, 72, 7)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === anObjectLiteral) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>anObjectLiteral : Symbol(anObjectLiteral, Decl(unknownType2.ts, 54, 13)) + + let uObjectLiteral: object = u; +>uObjectLiteral : Symbol(uObjectLiteral, Decl(unknownType2.ts, 76, 7)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === aUnion) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>aUnion : Symbol(aUnion, Decl(unknownType2.ts, 55, 13)) + + type unionDoesNotNarrow = isTrue> +>unionDoesNotNarrow : Symbol(unionDoesNotNarrow, Decl(unknownType2.ts, 79, 19)) +>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) +>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === anIntersection) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>anIntersection : Symbol(anIntersection, Decl(unknownType2.ts, 56, 13)) + + type intersectionDoesNotNarrow = isTrue> +>intersectionDoesNotNarrow : Symbol(intersectionDoesNotNarrow, Decl(unknownType2.ts, 83, 27)) +>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53)) +>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if (u === aFunction) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>aFunction : Symbol(aFunction, Decl(unknownType2.ts, 57, 13)) + + let uFunction: object = u; +>uFunction : Symbol(uFunction, Decl(unknownType2.ts, 88, 7)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +enum NumberEnum { +>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 89, 1)) + + A, +>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 91, 17)) + + B, +>B : Symbol(NumberEnum.B, Decl(unknownType2.ts, 92, 6)) + + C +>C : Symbol(NumberEnum.C, Decl(unknownType2.ts, 93, 6)) +} + +enum StringEnum { +>StringEnum : Symbol(StringEnum, Decl(unknownType2.ts, 95, 1)) + + A = "A", +>A : Symbol(StringEnum.A, Decl(unknownType2.ts, 97, 17)) + + B = "B", +>B : Symbol(StringEnum.B, Decl(unknownType2.ts, 98, 12)) + + C = "C" +>C : Symbol(StringEnum.C, Decl(unknownType2.ts, 99, 12)) +} + +if (u === NumberEnum || u === StringEnum) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 89, 1)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>StringEnum : Symbol(StringEnum, Decl(unknownType2.ts, 95, 1)) + + let enumObj: object = u; +>enumObj : Symbol(enumObj, Decl(unknownType2.ts, 104, 7)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if(u === NumberEnum.A) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>NumberEnum.A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 91, 17)) +>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 89, 1)) +>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 91, 17)) + + let a: NumberEnum.A = u +>a : Symbol(a, Decl(unknownType2.ts, 108, 7)) +>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 89, 1)) +>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 91, 17)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + +if(u === StringEnum.B) { +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +>StringEnum.B : Symbol(StringEnum.B, Decl(unknownType2.ts, 98, 12)) +>StringEnum : Symbol(StringEnum, Decl(unknownType2.ts, 95, 1)) +>B : Symbol(StringEnum.B, Decl(unknownType2.ts, 98, 12)) + + let b: StringEnum.B = u +>b : Symbol(b, Decl(unknownType2.ts, 112, 7)) +>StringEnum : Symbol(StringEnum, Decl(unknownType2.ts, 95, 1)) +>B : Symbol(StringEnum.B, Decl(unknownType2.ts, 98, 12)) +>u : Symbol(u, Decl(unknownType2.ts, 6, 5)) +} + diff --git a/tests/baselines/reference/unknownType2.types b/tests/baselines/reference/unknownType2.types index 6f9195b79946a..f90d85525cc50 100644 --- a/tests/baselines/reference/unknownType2.types +++ b/tests/baselines/reference/unknownType2.types @@ -105,7 +105,6 @@ if (!(u === 42)) { type A = isTrue> >A : true >u : unknown - } if (u !== 42) { @@ -148,3 +147,181 @@ if (u == Object) { >u : unknown } +declare const aString: string; +>aString : string + +declare const aBoolean: boolean; +>aBoolean : boolean + +declare const aNumber: number; +>aNumber : number + +declare const anObject: object; +>anObject : object + +declare const anObjectLiteral: { x: number }; +>anObjectLiteral : { x: number; } +>x : number + +declare const aUnion: { x: number } | { y: string }; +>aUnion : { x: number; } | { y: string; } +>x : number +>y : string + +declare const anIntersection: { x: number } & { y: string }; +>anIntersection : { x: number; } & { y: string; } +>x : number +>y : string + +declare const aFunction: () => number; +>aFunction : () => number + +if (u === aString) { +>u === aString : boolean +>u : unknown +>aString : string + + let uString: string = u; +>uString : string +>u : string +} + +if (u === aBoolean) { +>u === aBoolean : boolean +>u : unknown +>aBoolean : boolean + + let uString: boolean = u; +>uString : boolean +>u : boolean +} + +if (u === aNumber) { +>u === aNumber : boolean +>u : unknown +>aNumber : number + + let uNumber: number = u; +>uNumber : number +>u : number +} + +if (u === anObject) { +>u === anObject : boolean +>u : unknown +>anObject : object + + let uObject: object = u; +>uObject : object +>u : object +} + +if (u === anObjectLiteral) { +>u === anObjectLiteral : boolean +>u : unknown +>anObjectLiteral : { x: number; } + + let uObjectLiteral: object = u; +>uObjectLiteral : object +>u : object +} + +if (u === aUnion) { +>u === aUnion : boolean +>u : unknown +>aUnion : { x: number; } | { y: string; } + + type unionDoesNotNarrow = isTrue> +>unionDoesNotNarrow : true +>u : unknown +} + +if (u === anIntersection) { +>u === anIntersection : boolean +>u : unknown +>anIntersection : { x: number; } & { y: string; } + + type intersectionDoesNotNarrow = isTrue> +>intersectionDoesNotNarrow : true +>u : unknown +} + +if (u === aFunction) { +>u === aFunction : boolean +>u : unknown +>aFunction : () => number + + let uFunction: object = u; +>uFunction : object +>u : object +} + +enum NumberEnum { +>NumberEnum : NumberEnum + + A, +>A : NumberEnum.A + + B, +>B : NumberEnum.B + + C +>C : NumberEnum.C +} + +enum StringEnum { +>StringEnum : StringEnum + + A = "A", +>A : StringEnum.A +>"A" : "A" + + B = "B", +>B : StringEnum.B +>"B" : "B" + + C = "C" +>C : StringEnum.C +>"C" : "C" +} + +if (u === NumberEnum || u === StringEnum) { +>u === NumberEnum || u === StringEnum : boolean +>u === NumberEnum : boolean +>u : unknown +>NumberEnum : typeof NumberEnum +>u === StringEnum : boolean +>u : unknown +>StringEnum : typeof StringEnum + + let enumObj: object = u; +>enumObj : object +>u : object +} + +if(u === NumberEnum.A) { +>u === NumberEnum.A : boolean +>u : unknown +>NumberEnum.A : NumberEnum.A +>NumberEnum : typeof NumberEnum +>A : NumberEnum.A + + let a: NumberEnum.A = u +>a : NumberEnum.A +>NumberEnum : any +>u : NumberEnum.A +} + +if(u === StringEnum.B) { +>u === StringEnum.B : boolean +>u : unknown +>StringEnum.B : StringEnum.B +>StringEnum : typeof StringEnum +>B : StringEnum.B + + let b: StringEnum.B = u +>b : StringEnum.B +>StringEnum : any +>u : StringEnum.B +} + diff --git a/tests/cases/compiler/strictNullEmptyDestructuring.ts~ b/tests/cases/compiler/strictNullEmptyDestructuring.ts~ new file mode 100644 index 0000000000000..143216ec82448 --- /dev/null +++ b/tests/cases/compiler/strictNullEmptyDestructuring.ts~ @@ -0,0 +1,23 @@ +// @strictNullChecks: true + +// Repro from #20873 + +let { } = null; + +({} = null); + +let { } = undefined; + +({} = undefined); + +let { } = Math.random() ? {} : null; + +({} = Math.random() ? {} : null); + +let { } = Math.random() ? {} : undefined; + +({} = Math.random() ? {} : undefined); + +let { } = Math.random() ? null : undefined; + +({} = Math.random() ? null : undefined); diff --git a/tests/cases/conformance/types/unknown/unknownType2.ts b/tests/cases/conformance/types/unknown/unknownType2.ts index c0da9a4e782b3..f8700958d6bba 100644 --- a/tests/cases/conformance/types/unknown/unknownType2.ts +++ b/tests/cases/conformance/types/unknown/unknownType2.ts @@ -32,7 +32,6 @@ if (u === symb) { if (!(u === 42)) { type A = isTrue> - } if (u !== 42) { @@ -50,3 +49,68 @@ if (u == true) { if (u == Object) { type E = isTrue> } + +declare const aString: string; +declare const aBoolean: boolean; +declare const aNumber: number; +declare const anObject: object; +declare const anObjectLiteral: { x: number }; +declare const aUnion: { x: number } | { y: string }; +declare const anIntersection: { x: number } & { y: string }; +declare const aFunction: () => number; + +if (u === aString) { + let uString: string = u; +} + +if (u === aBoolean) { + let uString: boolean = u; +} + +if (u === aNumber) { + let uNumber: number = u; +} + +if (u === anObject) { + let uObject: object = u; +} + +if (u === anObjectLiteral) { + let uObjectLiteral: object = u; +} + +if (u === aUnion) { + type unionDoesNotNarrow = isTrue> +} + +if (u === anIntersection) { + type intersectionDoesNotNarrow = isTrue> +} + +if (u === aFunction) { + let uFunction: object = u; +} + +enum NumberEnum { + A, + B, + C +} + +enum StringEnum { + A = "A", + B = "B", + C = "C" +} + +if (u === NumberEnum || u === StringEnum) { + let enumObj: object = u; +} + +if(u === NumberEnum.A) { + let a: NumberEnum.A = u +} + +if(u === StringEnum.B) { + let b: StringEnum.B = u +} From b27679eee3c90421681f4dbc681890578d94005f Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Thu, 13 Sep 2018 19:33:21 +0100 Subject: [PATCH 4/5] Remove old test file that snuck in --- .../compiler/strictNullEmptyDestructuring.ts~ | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 tests/cases/compiler/strictNullEmptyDestructuring.ts~ diff --git a/tests/cases/compiler/strictNullEmptyDestructuring.ts~ b/tests/cases/compiler/strictNullEmptyDestructuring.ts~ deleted file mode 100644 index 143216ec82448..0000000000000 --- a/tests/cases/compiler/strictNullEmptyDestructuring.ts~ +++ /dev/null @@ -1,23 +0,0 @@ -// @strictNullChecks: true - -// Repro from #20873 - -let { } = null; - -({} = null); - -let { } = undefined; - -({} = undefined); - -let { } = Math.random() ? {} : null; - -({} = Math.random() ? {} : null); - -let { } = Math.random() ? {} : undefined; - -({} = Math.random() ? {} : undefined); - -let { } = Math.random() ? null : undefined; - -({} = Math.random() ? null : undefined); From 6f30537d13194ef9410850e26678251581abc357 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Tue, 18 Sep 2018 09:23:25 +0100 Subject: [PATCH 5/5] Simplify type flag check --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 29be16bd21f09..6788025cf5e21 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16019,7 +16019,7 @@ namespace ts { } const valueType = getTypeOfExpression(value); if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && assumeTrue) { - if (valueType.flags & TypeFlags.Primitive || valueType.flags & TypeFlags.NonPrimitive) { + if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) { return valueType; } if (valueType.flags & TypeFlags.Object) {