Fixed an issue in boolean comparison narrowing when the reference is an optional chain (#56504)

This commit is contained in:
Mateusz Burzyński
2023-11-22 11:01:10 -08:00
committed by GitHub
parent 6f9d34985d
commit d4fbc9b57d
5 changed files with 303 additions and 2 deletions
+2 -2
View File
@@ -27930,10 +27930,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (isMatchingConstructorReference(right)) {
return narrowTypeByConstructor(type, operator, left, assumeTrue);
}
if (isBooleanLiteral(right)) {
if (isBooleanLiteral(right) && !isAccessExpression(left)) {
return narrowTypeByBooleanComparison(type, left, right, operator, assumeTrue);
}
if (isBooleanLiteral(left)) {
if (isBooleanLiteral(left) && !isAccessExpression(right)) {
return narrowTypeByBooleanComparison(type, right, left, operator, assumeTrue);
}
break;
@@ -0,0 +1,49 @@
controlFlowOptionalChain3.tsx(30,8): error TS18048: 'foo' is possibly 'undefined'.
controlFlowOptionalChain3.tsx(36,31): error TS18048: 'options' is possibly 'undefined'.
==== controlFlowOptionalChain3.tsx (2 errors) ====
/// <reference path="/.lib/react16.d.ts" />
// https://github.com/microsoft/TypeScript/issues/56482
import React from "react";
interface Foo {
bar: boolean;
}
function test1(foo: Foo | undefined) {
if (foo?.bar === false) {
foo;
}
foo;
}
function test2(foo: Foo | undefined) {
if (foo?.bar === false) {
foo;
} else {
foo;
}
}
function Test3({ foo }: { foo: Foo | undefined }) {
return (
<div>
{foo?.bar === false && "foo"}
{foo.bar ? "true" : "false"}
~~~
!!! error TS18048: 'foo' is possibly 'undefined'.
</div>
);
}
function test4(options?: { a?: boolean; b?: boolean }) {
if (options?.a === false || options.b) {
~~~~~~~
!!! error TS18048: 'options' is possibly 'undefined'.
options;
}
}
@@ -0,0 +1,98 @@
//// [tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx] ////
=== controlFlowOptionalChain3.tsx ===
/// <reference path="react16.d.ts" />
// https://github.com/microsoft/TypeScript/issues/56482
import React from "react";
>React : Symbol(React, Decl(controlFlowOptionalChain3.tsx, 4, 6))
interface Foo {
>Foo : Symbol(Foo, Decl(controlFlowOptionalChain3.tsx, 4, 26))
bar: boolean;
>bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
}
function test1(foo: Foo | undefined) {
>test1 : Symbol(test1, Decl(controlFlowOptionalChain3.tsx, 8, 1))
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 10, 15))
>Foo : Symbol(Foo, Decl(controlFlowOptionalChain3.tsx, 4, 26))
if (foo?.bar === false) {
>foo?.bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 10, 15))
>bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
foo;
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 10, 15))
}
foo;
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 10, 15))
}
function test2(foo: Foo | undefined) {
>test2 : Symbol(test2, Decl(controlFlowOptionalChain3.tsx, 15, 1))
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 17, 15))
>Foo : Symbol(Foo, Decl(controlFlowOptionalChain3.tsx, 4, 26))
if (foo?.bar === false) {
>foo?.bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 17, 15))
>bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
foo;
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 17, 15))
} else {
foo;
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 17, 15))
}
}
function Test3({ foo }: { foo: Foo | undefined }) {
>Test3 : Symbol(Test3, Decl(controlFlowOptionalChain3.tsx, 23, 1))
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 25, 16))
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 25, 25))
>Foo : Symbol(Foo, Decl(controlFlowOptionalChain3.tsx, 4, 26))
return (
<div>
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
{foo?.bar === false && "foo"}
>foo?.bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 25, 16))
>bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
{foo.bar ? "true" : "false"}
>foo.bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
>foo : Symbol(foo, Decl(controlFlowOptionalChain3.tsx, 25, 16))
>bar : Symbol(Foo.bar, Decl(controlFlowOptionalChain3.tsx, 6, 15))
</div>
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
);
}
function test4(options?: { a?: boolean; b?: boolean }) {
>test4 : Symbol(test4, Decl(controlFlowOptionalChain3.tsx, 32, 1))
>options : Symbol(options, Decl(controlFlowOptionalChain3.tsx, 34, 15))
>a : Symbol(a, Decl(controlFlowOptionalChain3.tsx, 34, 26))
>b : Symbol(b, Decl(controlFlowOptionalChain3.tsx, 34, 39))
if (options?.a === false || options.b) {
>options?.a : Symbol(a, Decl(controlFlowOptionalChain3.tsx, 34, 26))
>options : Symbol(options, Decl(controlFlowOptionalChain3.tsx, 34, 15))
>a : Symbol(a, Decl(controlFlowOptionalChain3.tsx, 34, 26))
>options.b : Symbol(b, Decl(controlFlowOptionalChain3.tsx, 34, 39))
>options : Symbol(options, Decl(controlFlowOptionalChain3.tsx, 34, 15))
>b : Symbol(b, Decl(controlFlowOptionalChain3.tsx, 34, 39))
options;
>options : Symbol(options, Decl(controlFlowOptionalChain3.tsx, 34, 15))
}
}
@@ -0,0 +1,110 @@
//// [tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx] ////
=== controlFlowOptionalChain3.tsx ===
/// <reference path="react16.d.ts" />
// https://github.com/microsoft/TypeScript/issues/56482
import React from "react";
>React : typeof React
interface Foo {
bar: boolean;
>bar : boolean
}
function test1(foo: Foo | undefined) {
>test1 : (foo: Foo | undefined) => void
>foo : Foo | undefined
if (foo?.bar === false) {
>foo?.bar === false : boolean
>foo?.bar : boolean | undefined
>foo : Foo | undefined
>bar : boolean | undefined
>false : false
foo;
>foo : Foo
}
foo;
>foo : Foo | undefined
}
function test2(foo: Foo | undefined) {
>test2 : (foo: Foo | undefined) => void
>foo : Foo | undefined
if (foo?.bar === false) {
>foo?.bar === false : boolean
>foo?.bar : boolean | undefined
>foo : Foo | undefined
>bar : boolean | undefined
>false : false
foo;
>foo : Foo
} else {
foo;
>foo : Foo | undefined
}
}
function Test3({ foo }: { foo: Foo | undefined }) {
>Test3 : ({ foo }: { foo: Foo | undefined; }) => JSX.Element
>foo : Foo | undefined
>foo : Foo | undefined
return (
>( <div> {foo?.bar === false && "foo"} {foo.bar ? "true" : "false"} </div> ) : JSX.Element
<div>
><div> {foo?.bar === false && "foo"} {foo.bar ? "true" : "false"} </div> : JSX.Element
>div : any
{foo?.bar === false && "foo"}
>foo?.bar === false && "foo" : false | "foo"
>foo?.bar === false : boolean
>foo?.bar : boolean | undefined
>foo : Foo | undefined
>bar : boolean | undefined
>false : false
>"foo" : "foo"
{foo.bar ? "true" : "false"}
>foo.bar ? "true" : "false" : "false" | "true"
>foo.bar : boolean
>foo : Foo | undefined
>bar : boolean
>"true" : "true"
>"false" : "false"
</div>
>div : any
);
}
function test4(options?: { a?: boolean; b?: boolean }) {
>test4 : (options?: { a?: boolean; b?: boolean;}) => void
>options : { a?: boolean | undefined; b?: boolean | undefined; } | undefined
>a : boolean | undefined
>b : boolean | undefined
if (options?.a === false || options.b) {
>options?.a === false || options.b : boolean | undefined
>options?.a === false : boolean
>options?.a : boolean | undefined
>options : { a?: boolean | undefined; b?: boolean | undefined; } | undefined
>a : boolean | undefined
>false : false
>options.b : boolean | undefined
>options : { a?: boolean | undefined; b?: boolean | undefined; } | undefined
>b : boolean | undefined
options;
>options : { a?: boolean | undefined; b?: boolean | undefined; } | undefined
}
}
@@ -0,0 +1,44 @@
// @strict: true
// @noEmit: true
// @esModuleInterop: true
// @jsx: react
/// <reference path="/.lib/react16.d.ts" />
// https://github.com/microsoft/TypeScript/issues/56482
import React from "react";
interface Foo {
bar: boolean;
}
function test1(foo: Foo | undefined) {
if (foo?.bar === false) {
foo;
}
foo;
}
function test2(foo: Foo | undefined) {
if (foo?.bar === false) {
foo;
} else {
foo;
}
}
function Test3({ foo }: { foo: Foo | undefined }) {
return (
<div>
{foo?.bar === false && "foo"}
{foo.bar ? "true" : "false"}
</div>
);
}
function test4(options?: { a?: boolean; b?: boolean }) {
if (options?.a === false || options.b) {
options;
}
}