From 672d2144fa8ff41612e2cce196ce0f85701c337a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 30 Apr 2018 10:43:43 -0700 Subject: [PATCH 1/3] Check for null type instead of null keyword --- 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 ae59997b7a2..9bd940e547e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13690,7 +13690,7 @@ namespace ts { const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; const facts = doubleEquals ? assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull : - value.kind === SyntaxKind.NullKeyword ? + valueType.flags & TypeFlags.Null ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; return getTypeWithFacts(type, facts); From 0dc42fea743f5ca50a667a6e604fa85832492df0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 30 Apr 2018 10:43:55 -0700 Subject: [PATCH 2/3] Add regression tests --- .../compiler/controlFlowNullTypeAndLiteral.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/cases/compiler/controlFlowNullTypeAndLiteral.ts diff --git a/tests/cases/compiler/controlFlowNullTypeAndLiteral.ts b/tests/cases/compiler/controlFlowNullTypeAndLiteral.ts new file mode 100644 index 00000000000..ee66f441f14 --- /dev/null +++ b/tests/cases/compiler/controlFlowNullTypeAndLiteral.ts @@ -0,0 +1,25 @@ +// @strict: true + +// Repros from #23771 + +const myNull: null = null; +const objWithValMaybeNull: { val: number | null } = { val: 1 }; +const addOne = function (num: number) { + return num + 1; +} + +if (objWithValMaybeNull.val !== null) + addOne(objWithValMaybeNull.val); +if (objWithValMaybeNull.val !== myNull) + addOne(objWithValMaybeNull.val); + +if (objWithValMaybeNull.val === null) + addOne(objWithValMaybeNull.val); // Error +if (objWithValMaybeNull.val === myNull) + addOne(objWithValMaybeNull.val); // Error + +function f(x: number | null) { + if(x === myNull) { + const s: string = x; // Error + } +} From a286cffb5bed0eaa9565bd59c1f31058a94e4fc3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 30 Apr 2018 10:44:53 -0700 Subject: [PATCH 3/3] Accept new baselines --- .../controlFlowNullTypeAndLiteral.errors.txt | 36 +++++++ .../controlFlowNullTypeAndLiteral.js | 47 +++++++++ .../controlFlowNullTypeAndLiteral.symbols | 79 +++++++++++++++ .../controlFlowNullTypeAndLiteral.types | 99 +++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 tests/baselines/reference/controlFlowNullTypeAndLiteral.errors.txt create mode 100644 tests/baselines/reference/controlFlowNullTypeAndLiteral.js create mode 100644 tests/baselines/reference/controlFlowNullTypeAndLiteral.symbols create mode 100644 tests/baselines/reference/controlFlowNullTypeAndLiteral.types diff --git a/tests/baselines/reference/controlFlowNullTypeAndLiteral.errors.txt b/tests/baselines/reference/controlFlowNullTypeAndLiteral.errors.txt new file mode 100644 index 00000000000..5a74b4f2d65 --- /dev/null +++ b/tests/baselines/reference/controlFlowNullTypeAndLiteral.errors.txt @@ -0,0 +1,36 @@ +tests/cases/compiler/controlFlowNullTypeAndLiteral.ts(15,12): error TS2345: Argument of type 'null' is not assignable to parameter of type 'number'. +tests/cases/compiler/controlFlowNullTypeAndLiteral.ts(17,12): error TS2345: Argument of type 'null' is not assignable to parameter of type 'number'. +tests/cases/compiler/controlFlowNullTypeAndLiteral.ts(21,15): error TS2322: Type 'null' is not assignable to type 'string'. + + +==== tests/cases/compiler/controlFlowNullTypeAndLiteral.ts (3 errors) ==== + // Repros from #23771 + + const myNull: null = null; + const objWithValMaybeNull: { val: number | null } = { val: 1 }; + const addOne = function (num: number) { + return num + 1; + } + + if (objWithValMaybeNull.val !== null) + addOne(objWithValMaybeNull.val); + if (objWithValMaybeNull.val !== myNull) + addOne(objWithValMaybeNull.val); + + if (objWithValMaybeNull.val === null) + addOne(objWithValMaybeNull.val); // Error + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type 'null' is not assignable to parameter of type 'number'. + if (objWithValMaybeNull.val === myNull) + addOne(objWithValMaybeNull.val); // Error + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type 'null' is not assignable to parameter of type 'number'. + + function f(x: number | null) { + if(x === myNull) { + const s: string = x; // Error + ~ +!!! error TS2322: Type 'null' is not assignable to type 'string'. + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowNullTypeAndLiteral.js b/tests/baselines/reference/controlFlowNullTypeAndLiteral.js new file mode 100644 index 00000000000..dc23aeddf27 --- /dev/null +++ b/tests/baselines/reference/controlFlowNullTypeAndLiteral.js @@ -0,0 +1,47 @@ +//// [controlFlowNullTypeAndLiteral.ts] +// Repros from #23771 + +const myNull: null = null; +const objWithValMaybeNull: { val: number | null } = { val: 1 }; +const addOne = function (num: number) { + return num + 1; +} + +if (objWithValMaybeNull.val !== null) + addOne(objWithValMaybeNull.val); +if (objWithValMaybeNull.val !== myNull) + addOne(objWithValMaybeNull.val); + +if (objWithValMaybeNull.val === null) + addOne(objWithValMaybeNull.val); // Error +if (objWithValMaybeNull.val === myNull) + addOne(objWithValMaybeNull.val); // Error + +function f(x: number | null) { + if(x === myNull) { + const s: string = x; // Error + } +} + + +//// [controlFlowNullTypeAndLiteral.js] +"use strict"; +// Repros from #23771 +var myNull = null; +var objWithValMaybeNull = { val: 1 }; +var addOne = function (num) { + return num + 1; +}; +if (objWithValMaybeNull.val !== null) + addOne(objWithValMaybeNull.val); +if (objWithValMaybeNull.val !== myNull) + addOne(objWithValMaybeNull.val); +if (objWithValMaybeNull.val === null) + addOne(objWithValMaybeNull.val); // Error +if (objWithValMaybeNull.val === myNull) + addOne(objWithValMaybeNull.val); // Error +function f(x) { + if (x === myNull) { + var s = x; // Error + } +} diff --git a/tests/baselines/reference/controlFlowNullTypeAndLiteral.symbols b/tests/baselines/reference/controlFlowNullTypeAndLiteral.symbols new file mode 100644 index 00000000000..d78f38975ab --- /dev/null +++ b/tests/baselines/reference/controlFlowNullTypeAndLiteral.symbols @@ -0,0 +1,79 @@ +=== tests/cases/compiler/controlFlowNullTypeAndLiteral.ts === +// Repros from #23771 + +const myNull: null = null; +>myNull : Symbol(myNull, Decl(controlFlowNullTypeAndLiteral.ts, 2, 5)) + +const objWithValMaybeNull: { val: number | null } = { val: 1 }; +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 53)) + +const addOne = function (num: number) { +>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5)) +>num : Symbol(num, Decl(controlFlowNullTypeAndLiteral.ts, 4, 25)) + + return num + 1; +>num : Symbol(num, Decl(controlFlowNullTypeAndLiteral.ts, 4, 25)) +} + +if (objWithValMaybeNull.val !== null) +>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) + + addOne(objWithValMaybeNull.val); +>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5)) +>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) + +if (objWithValMaybeNull.val !== myNull) +>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>myNull : Symbol(myNull, Decl(controlFlowNullTypeAndLiteral.ts, 2, 5)) + + addOne(objWithValMaybeNull.val); +>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5)) +>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) + +if (objWithValMaybeNull.val === null) +>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) + + addOne(objWithValMaybeNull.val); // Error +>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5)) +>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) + +if (objWithValMaybeNull.val === myNull) +>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>myNull : Symbol(myNull, Decl(controlFlowNullTypeAndLiteral.ts, 2, 5)) + + addOne(objWithValMaybeNull.val); // Error +>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5)) +>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) +>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5)) +>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28)) + +function f(x: number | null) { +>f : Symbol(f, Decl(controlFlowNullTypeAndLiteral.ts, 16, 36)) +>x : Symbol(x, Decl(controlFlowNullTypeAndLiteral.ts, 18, 11)) + + if(x === myNull) { +>x : Symbol(x, Decl(controlFlowNullTypeAndLiteral.ts, 18, 11)) +>myNull : Symbol(myNull, Decl(controlFlowNullTypeAndLiteral.ts, 2, 5)) + + const s: string = x; // Error +>s : Symbol(s, Decl(controlFlowNullTypeAndLiteral.ts, 20, 13)) +>x : Symbol(x, Decl(controlFlowNullTypeAndLiteral.ts, 18, 11)) + } +} + diff --git a/tests/baselines/reference/controlFlowNullTypeAndLiteral.types b/tests/baselines/reference/controlFlowNullTypeAndLiteral.types new file mode 100644 index 00000000000..0c0afe63d18 --- /dev/null +++ b/tests/baselines/reference/controlFlowNullTypeAndLiteral.types @@ -0,0 +1,99 @@ +=== tests/cases/compiler/controlFlowNullTypeAndLiteral.ts === +// Repros from #23771 + +const myNull: null = null; +>myNull : null +>null : null +>null : null + +const objWithValMaybeNull: { val: number | null } = { val: 1 }; +>objWithValMaybeNull : { val: number | null; } +>val : number | null +>null : null +>{ val: 1 } : { val: number; } +>val : number +>1 : 1 + +const addOne = function (num: number) { +>addOne : (num: number) => number +>function (num: number) { return num + 1;} : (num: number) => number +>num : number + + return num + 1; +>num + 1 : number +>num : number +>1 : 1 +} + +if (objWithValMaybeNull.val !== null) +>objWithValMaybeNull.val !== null : boolean +>objWithValMaybeNull.val : number | null +>objWithValMaybeNull : { val: number | null; } +>val : number | null +>null : null + + addOne(objWithValMaybeNull.val); +>addOne(objWithValMaybeNull.val) : number +>addOne : (num: number) => number +>objWithValMaybeNull.val : number +>objWithValMaybeNull : { val: number | null; } +>val : number + +if (objWithValMaybeNull.val !== myNull) +>objWithValMaybeNull.val !== myNull : boolean +>objWithValMaybeNull.val : number | null +>objWithValMaybeNull : { val: number | null; } +>val : number | null +>myNull : null + + addOne(objWithValMaybeNull.val); +>addOne(objWithValMaybeNull.val) : number +>addOne : (num: number) => number +>objWithValMaybeNull.val : number +>objWithValMaybeNull : { val: number | null; } +>val : number + +if (objWithValMaybeNull.val === null) +>objWithValMaybeNull.val === null : boolean +>objWithValMaybeNull.val : number | null +>objWithValMaybeNull : { val: number | null; } +>val : number | null +>null : null + + addOne(objWithValMaybeNull.val); // Error +>addOne(objWithValMaybeNull.val) : number +>addOne : (num: number) => number +>objWithValMaybeNull.val : null +>objWithValMaybeNull : { val: number | null; } +>val : null + +if (objWithValMaybeNull.val === myNull) +>objWithValMaybeNull.val === myNull : boolean +>objWithValMaybeNull.val : number | null +>objWithValMaybeNull : { val: number | null; } +>val : number | null +>myNull : null + + addOne(objWithValMaybeNull.val); // Error +>addOne(objWithValMaybeNull.val) : number +>addOne : (num: number) => number +>objWithValMaybeNull.val : null +>objWithValMaybeNull : { val: number | null; } +>val : null + +function f(x: number | null) { +>f : (x: number | null) => void +>x : number | null +>null : null + + if(x === myNull) { +>x === myNull : boolean +>x : number | null +>myNull : null + + const s: string = x; // Error +>s : string +>x : null + } +} +