From f3808c416d42f20093121b70d3f0a65b4d026b1f Mon Sep 17 00:00:00 2001 From: TypeScript Bot Date: Wed, 22 Nov 2023 11:10:25 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20Pick=20PR=20#56504=20(Fixed=20an?= =?UTF-8?q?=20issue=20in=20boolean=20compariso...)=20into=20release-5.3=20?= =?UTF-8?q?(#56512)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mateusz BurzyƄski --- src/compiler/checker.ts | 4 +- .../controlFlowOptionalChain3.errors.txt | 49 ++++++++ .../controlFlowOptionalChain3.symbols | 98 ++++++++++++++++ .../reference/controlFlowOptionalChain3.types | 110 ++++++++++++++++++ .../controlFlow/controlFlowOptionalChain3.tsx | 44 +++++++ 5 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/controlFlowOptionalChain3.errors.txt create mode 100644 tests/baselines/reference/controlFlowOptionalChain3.symbols create mode 100644 tests/baselines/reference/controlFlowOptionalChain3.types create mode 100644 tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9ad699f1adcb0..dea6d726160d2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27886,10 +27886,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; diff --git a/tests/baselines/reference/controlFlowOptionalChain3.errors.txt b/tests/baselines/reference/controlFlowOptionalChain3.errors.txt new file mode 100644 index 0000000000000..15a8997f0376e --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain3.errors.txt @@ -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) ==== + /// + + // 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 ( +
+ {foo?.bar === false && "foo"} + {foo.bar ? "true" : "false"} + ~~~ +!!! error TS18048: 'foo' is possibly 'undefined'. +
+ ); + } + + function test4(options?: { a?: boolean; b?: boolean }) { + if (options?.a === false || options.b) { + ~~~~~~~ +!!! error TS18048: 'options' is possibly 'undefined'. + options; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowOptionalChain3.symbols b/tests/baselines/reference/controlFlowOptionalChain3.symbols new file mode 100644 index 0000000000000..714e32bd6e1ee --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain3.symbols @@ -0,0 +1,98 @@ +//// [tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx] //// + +=== controlFlowOptionalChain3.tsx === +/// + +// 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 : 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 : 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)) + } +} + diff --git a/tests/baselines/reference/controlFlowOptionalChain3.types b/tests/baselines/reference/controlFlowOptionalChain3.types new file mode 100644 index 0000000000000..2ef7dc502c01a --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain3.types @@ -0,0 +1,110 @@ +//// [tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx] //// + +=== controlFlowOptionalChain3.tsx === +/// + +// 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 ( +>(
{foo?.bar === false && "foo"} {foo.bar ? "true" : "false"}
) : JSX.Element + +
+>
{foo?.bar === false && "foo"} {foo.bar ? "true" : "false"}
: 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 : 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 + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx b/tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx new file mode 100644 index 0000000000000..191de7ed68255 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx @@ -0,0 +1,44 @@ +// @strict: true +// @noEmit: true +// @esModuleInterop: true +// @jsx: react + +/// + +// 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 ( +
+ {foo?.bar === false && "foo"} + {foo.bar ? "true" : "false"} +
+ ); +} + +function test4(options?: { a?: boolean; b?: boolean }) { + if (options?.a === false || options.b) { + options; + } +}