fix(51760): Nullish-coalescing assignment narrows type to assigned value (#51767)

* fix(51760): skip type guard errors for narrowed nullish-coalescing assignment

* update tests
This commit is contained in:
Oleksandr T
2022-12-30 02:36:59 +02:00
committed by GitHub
parent c7f49bceed
commit ba393b6b9f
8 changed files with 280 additions and 1 deletions

View File

@@ -26819,7 +26819,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type {
// for `a?.b`, we emulate a synthetic `a !== null && a !== undefined` condition for `a`
if (isExpressionOfOptionalChainRoot(expr) ||
isBinaryExpression(expr.parent) && expr.parent.operatorToken.kind === SyntaxKind.QuestionQuestionToken && expr.parent.left === expr) {
isBinaryExpression(expr.parent) && (expr.parent.operatorToken.kind === SyntaxKind.QuestionQuestionToken || expr.parent.operatorToken.kind === SyntaxKind.QuestionQuestionEqualsToken) && expr.parent.left === expr) {
return narrowTypeByOptionality(type, expr, assumeTrue);
}
switch (expr.kind) {

View File

@@ -0,0 +1,34 @@
//// [equalityWithtNullishCoalescingAssignment.ts]
function f1(a?: boolean): void {
a ??= true;
if (a === false) {
console.log(a);
}
}
f1(false);
function f2() {
let x: 0 | 1 | 2 | 3 = 0 as any;
x ??= 1;
if (x === 0) {
console.log(x);
}
}
//// [equalityWithtNullishCoalescingAssignment.js]
function f1(a) {
a !== null && a !== void 0 ? a : (a = true);
if (a === false) {
console.log(a);
}
}
f1(false);
function f2() {
var x = 0;
x !== null && x !== void 0 ? x : (x = 1);
if (x === 0) {
console.log(x);
}
}

View File

@@ -0,0 +1,41 @@
=== tests/cases/conformance/types/typeRelationships/comparable/equalityWithtNullishCoalescingAssignment.ts ===
function f1(a?: boolean): void {
>f1 : Symbol(f1, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 0))
>a : Symbol(a, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 12))
a ??= true;
>a : Symbol(a, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 12))
if (a === false) {
>a : Symbol(a, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 12))
console.log(a);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>a : Symbol(a, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 12))
}
}
f1(false);
>f1 : Symbol(f1, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 0))
function f2() {
>f2 : Symbol(f2, Decl(equalityWithtNullishCoalescingAssignment.ts, 7, 10))
let x: 0 | 1 | 2 | 3 = 0 as any;
>x : Symbol(x, Decl(equalityWithtNullishCoalescingAssignment.ts, 10, 7))
x ??= 1;
>x : Symbol(x, Decl(equalityWithtNullishCoalescingAssignment.ts, 10, 7))
if (x === 0) {
>x : Symbol(x, Decl(equalityWithtNullishCoalescingAssignment.ts, 10, 7))
console.log(x);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>x : Symbol(x, Decl(equalityWithtNullishCoalescingAssignment.ts, 10, 7))
}
}

View File

@@ -0,0 +1,55 @@
=== tests/cases/conformance/types/typeRelationships/comparable/equalityWithtNullishCoalescingAssignment.ts ===
function f1(a?: boolean): void {
>f1 : (a?: boolean) => void
>a : boolean
a ??= true;
>a ??= true : boolean
>a : boolean
>true : true
if (a === false) {
>a === false : boolean
>a : boolean
>false : false
console.log(a);
>console.log(a) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>a : false
}
}
f1(false);
>f1(false) : void
>f1 : (a?: boolean) => void
>false : false
function f2() {
>f2 : () => void
let x: 0 | 1 | 2 | 3 = 0 as any;
>x : 0 | 1 | 2 | 3
>0 as any : any
>0 : 0
x ??= 1;
>x ??= 1 : 0 | 1 | 2 | 3
>x : 0 | 1 | 2 | 3
>1 : 1
if (x === 0) {
>x === 0 : boolean
>x : 0 | 1 | 2 | 3
>0 : 0
console.log(x);
>console.log(x) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>x : 0
}
}

View File

@@ -0,0 +1,35 @@
//// [equalityWithtNullishCoalescingAssignment.ts]
function f1(a?: boolean): void {
a ??= true;
if (a === false) {
console.log(a);
}
}
f1(false);
function f2() {
let x: 0 | 1 | 2 | 3 = 0 as any;
x ??= 1;
if (x === 0) {
console.log(x);
}
}
//// [equalityWithtNullishCoalescingAssignment.js]
"use strict";
function f1(a) {
a !== null && a !== void 0 ? a : (a = true);
if (a === false) {
console.log(a);
}
}
f1(false);
function f2() {
var x = 0;
x !== null && x !== void 0 ? x : (x = 1);
if (x === 0) {
console.log(x);
}
}

View File

@@ -0,0 +1,41 @@
=== tests/cases/conformance/types/typeRelationships/comparable/equalityWithtNullishCoalescingAssignment.ts ===
function f1(a?: boolean): void {
>f1 : Symbol(f1, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 0))
>a : Symbol(a, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 12))
a ??= true;
>a : Symbol(a, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 12))
if (a === false) {
>a : Symbol(a, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 12))
console.log(a);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>a : Symbol(a, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 12))
}
}
f1(false);
>f1 : Symbol(f1, Decl(equalityWithtNullishCoalescingAssignment.ts, 0, 0))
function f2() {
>f2 : Symbol(f2, Decl(equalityWithtNullishCoalescingAssignment.ts, 7, 10))
let x: 0 | 1 | 2 | 3 = 0 as any;
>x : Symbol(x, Decl(equalityWithtNullishCoalescingAssignment.ts, 10, 7))
x ??= 1;
>x : Symbol(x, Decl(equalityWithtNullishCoalescingAssignment.ts, 10, 7))
if (x === 0) {
>x : Symbol(x, Decl(equalityWithtNullishCoalescingAssignment.ts, 10, 7))
console.log(x);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>x : Symbol(x, Decl(equalityWithtNullishCoalescingAssignment.ts, 10, 7))
}
}

View File

@@ -0,0 +1,55 @@
=== tests/cases/conformance/types/typeRelationships/comparable/equalityWithtNullishCoalescingAssignment.ts ===
function f1(a?: boolean): void {
>f1 : (a?: boolean) => void
>a : boolean | undefined
a ??= true;
>a ??= true : boolean
>a : boolean | undefined
>true : true
if (a === false) {
>a === false : boolean
>a : boolean
>false : false
console.log(a);
>console.log(a) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>a : false
}
}
f1(false);
>f1(false) : void
>f1 : (a?: boolean | undefined) => void
>false : false
function f2() {
>f2 : () => void
let x: 0 | 1 | 2 | 3 = 0 as any;
>x : 0 | 1 | 2 | 3
>0 as any : any
>0 : 0
x ??= 1;
>x ??= 1 : 0 | 1 | 2 | 3
>x : 0 | 1 | 2 | 3
>1 : 1
if (x === 0) {
>x === 0 : boolean
>x : 0 | 1 | 2 | 3
>0 : 0
console.log(x);
>console.log(x) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>x : 0
}
}

View File

@@ -0,0 +1,18 @@
// @strict: true, false
function f1(a?: boolean): void {
a ??= true;
if (a === false) {
console.log(a);
}
}
f1(false);
function f2() {
let x: 0 | 1 | 2 | 3 = 0 as any;
x ??= 1;
if (x === 0) {
console.log(x);
}
}