From 36cc154985992db89214f1c51165b24c36855226 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Thu, 13 Sep 2018 19:28:40 +0100 Subject: [PATCH] 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 96d6437e62e..29be16bd21f 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 a1305e823b8..66b763efb58 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 8c3f8a6e04a..577b1186def 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 6f9195b7994..f90d85525cc 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 00000000000..143216ec824 --- /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 c0da9a4e782..f8700958d6b 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 +}