diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 38127a8d62075..bbc8368c7d9c7 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -136,6 +136,7 @@ import { isBlock, isBlockOrCatchScoped, IsBlockScopedContainer, + isBooleanLiteral, isCallExpression, isClassStaticBlockDeclaration, isConditionalTypeNode, @@ -1279,7 +1280,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: return isNarrowableOperand(expr.left) || isNarrowableOperand(expr.right) || - isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right); + isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right) || + (isBooleanLiteral(expr.right) && isNarrowingExpression(expr.left) || isBooleanLiteral(expr.left) && isNarrowingExpression(expr.right)); case SyntaxKind.InstanceOfKeyword: return isNarrowableOperand(expr.left); case SyntaxKind.InKeyword: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6f4c2dd1d9094..6072d0db52cb8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34,6 +34,7 @@ import { BigIntLiteral, BigIntLiteralType, BinaryExpression, + BinaryOperator, BinaryOperatorToken, binarySearch, BindableObjectDefinePropertyCall, @@ -44,6 +45,7 @@ import { BindingPattern, bindSourceFile, Block, + BooleanLiteral, BreakOrContinueStatement, CallChain, CallExpression, @@ -27610,6 +27612,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type; } + function narrowTypeByBooleanComparison(type: Type, expr: Expression, bool: BooleanLiteral, operator: BinaryOperator, assumeTrue: boolean): Type { + assumeTrue = (assumeTrue !== (bool.kind === SyntaxKind.TrueKeyword)) !== (operator !== SyntaxKind.ExclamationEqualsEqualsToken && operator !== SyntaxKind.ExclamationEqualsToken); + return narrowType(type, expr, assumeTrue); + } + function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { switch (expr.operatorToken.kind) { case SyntaxKind.EqualsToken: @@ -27658,6 +27665,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isMatchingConstructorReference(right)) { return narrowTypeByConstructor(type, operator, left, assumeTrue); } + if (isBooleanLiteral(right)) { + return narrowTypeByBooleanComparison(type, left, right, operator, assumeTrue); + } + if (isBooleanLiteral(left)) { + return narrowTypeByBooleanComparison(type, right, left, operator, assumeTrue); + } break; case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); diff --git a/tests/baselines/reference/narrowByBooleanComparison.symbols b/tests/baselines/reference/narrowByBooleanComparison.symbols new file mode 100644 index 0000000000000..466d5936028bd --- /dev/null +++ b/tests/baselines/reference/narrowByBooleanComparison.symbols @@ -0,0 +1,264 @@ +//// [tests/cases/compiler/narrowByBooleanComparison.ts] //// + +=== narrowByBooleanComparison.ts === +type A = { type: "A" }; +>A : Symbol(A, Decl(narrowByBooleanComparison.ts, 0, 0)) +>type : Symbol(type, Decl(narrowByBooleanComparison.ts, 0, 10)) + +type B = { type: "B" }; +>B : Symbol(B, Decl(narrowByBooleanComparison.ts, 0, 23)) +>type : Symbol(type, Decl(narrowByBooleanComparison.ts, 1, 10)) + +type C = { type: "C" }; +>C : Symbol(C, Decl(narrowByBooleanComparison.ts, 1, 23)) +>type : Symbol(type, Decl(narrowByBooleanComparison.ts, 2, 10)) + +type MyUnion = A | B | C; +>MyUnion : Symbol(MyUnion, Decl(narrowByBooleanComparison.ts, 2, 23)) +>A : Symbol(A, Decl(narrowByBooleanComparison.ts, 0, 0)) +>B : Symbol(B, Decl(narrowByBooleanComparison.ts, 0, 23)) +>C : Symbol(C, Decl(narrowByBooleanComparison.ts, 1, 23)) + +const isA = (x: MyUnion): x is A => x.type === "A"; +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 5, 13)) +>MyUnion : Symbol(MyUnion, Decl(narrowByBooleanComparison.ts, 2, 23)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 5, 13)) +>A : Symbol(A, Decl(narrowByBooleanComparison.ts, 0, 0)) +>x.type : Symbol(type, Decl(narrowByBooleanComparison.ts, 0, 10), Decl(narrowByBooleanComparison.ts, 1, 10), Decl(narrowByBooleanComparison.ts, 2, 10)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 5, 13)) +>type : Symbol(type, Decl(narrowByBooleanComparison.ts, 0, 10), Decl(narrowByBooleanComparison.ts, 1, 10), Decl(narrowByBooleanComparison.ts, 2, 10)) + +function test1(x: MyUnion) { +>test1 : Symbol(test1, Decl(narrowByBooleanComparison.ts, 5, 51)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) +>MyUnion : Symbol(MyUnion, Decl(narrowByBooleanComparison.ts, 2, 23)) + + if (isA(x) !== true) { +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + } + + if (isA(x) !== false) { +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + } + + if (isA(x) === false) { +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + } + + if (isA(x) === true) { +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + } + + if (isA(x) != true) { +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + } + + if (isA(x) == true) { +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + } + + if (true !== isA(x)) { +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + } + + if (true === isA(x)) { +>isA : Symbol(isA, Decl(narrowByBooleanComparison.ts, 5, 5)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 7, 15)) + } +} + +// https://github.com/microsoft/TypeScript/issues/53093 +function test2(x: unknown) { +>test2 : Symbol(test2, Decl(narrowByBooleanComparison.ts, 39, 1)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 42, 15)) + + if (x instanceof Error === false) { +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 42, 15)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + return; + } + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 42, 15)) +} + +// https://github.com/microsoft/TypeScript/issues/50712 +function test3(foo: unknown) { +>test3 : Symbol(test3, Decl(narrowByBooleanComparison.ts, 47, 1)) +>foo : Symbol(foo, Decl(narrowByBooleanComparison.ts, 50, 15)) + + if (typeof foo !== 'string' && Array.isArray(foo) === false) { +>foo : Symbol(foo, Decl(narrowByBooleanComparison.ts, 50, 15)) +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>foo : Symbol(foo, Decl(narrowByBooleanComparison.ts, 50, 15)) + + throw new Error('Not a string or an array'); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + } + foo; +>foo : Symbol(foo, Decl(narrowByBooleanComparison.ts, 50, 15)) +} + +// https://github.com/microsoft/TypeScript/issues/55395 +class WebError extends URIError { +>WebError : Symbol(WebError, Decl(narrowByBooleanComparison.ts, 55, 1)) +>URIError : Symbol(URIError, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + status?: number; +>status : Symbol(WebError.status, Decl(narrowByBooleanComparison.ts, 58, 33)) +} +function test4() { +>test4 : Symbol(test4, Decl(narrowByBooleanComparison.ts, 60, 1)) + + try { + // make a request + } catch (err) { +>err : Symbol(err, Decl(narrowByBooleanComparison.ts, 64, 13)) + + if (err instanceof WebError === false || err.status != 401) { +>err : Symbol(err, Decl(narrowByBooleanComparison.ts, 64, 13)) +>WebError : Symbol(WebError, Decl(narrowByBooleanComparison.ts, 55, 1)) +>err.status : Symbol(WebError.status, Decl(narrowByBooleanComparison.ts, 58, 33)) +>err : Symbol(err, Decl(narrowByBooleanComparison.ts, 64, 13)) +>status : Symbol(WebError.status, Decl(narrowByBooleanComparison.ts, 58, 33)) + + console.error(err); +>console.error : Symbol(Console.error, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>error : Symbol(Console.error, Decl(lib.dom.d.ts, --, --)) +>err : Symbol(err, Decl(narrowByBooleanComparison.ts, 64, 13)) + } + } +} + +// https://github.com/microsoft/TypeScript/issues/44366 +interface Entity { +>Entity : Symbol(Entity, Decl(narrowByBooleanComparison.ts, 69, 1)) + + type: string; +>type : Symbol(Entity.type, Decl(narrowByBooleanComparison.ts, 72, 18)) +} +const ACTOR_TYPE = "actor"; +>ACTOR_TYPE : Symbol(ACTOR_TYPE, Decl(narrowByBooleanComparison.ts, 75, 5)) + +interface Actor extends Entity { +>Actor : Symbol(Actor, Decl(narrowByBooleanComparison.ts, 75, 27)) +>Entity : Symbol(Entity, Decl(narrowByBooleanComparison.ts, 69, 1)) + + type: typeof ACTOR_TYPE; +>type : Symbol(Actor.type, Decl(narrowByBooleanComparison.ts, 76, 32)) +>ACTOR_TYPE : Symbol(ACTOR_TYPE, Decl(narrowByBooleanComparison.ts, 75, 5)) +} +function isActor(entity: Entity): entity is Actor { +>isActor : Symbol(isActor, Decl(narrowByBooleanComparison.ts, 78, 1)) +>entity : Symbol(entity, Decl(narrowByBooleanComparison.ts, 79, 17)) +>Entity : Symbol(Entity, Decl(narrowByBooleanComparison.ts, 69, 1)) +>entity : Symbol(entity, Decl(narrowByBooleanComparison.ts, 79, 17)) +>Actor : Symbol(Actor, Decl(narrowByBooleanComparison.ts, 75, 27)) + + return entity.type === ACTOR_TYPE; +>entity.type : Symbol(Entity.type, Decl(narrowByBooleanComparison.ts, 72, 18)) +>entity : Symbol(entity, Decl(narrowByBooleanComparison.ts, 79, 17)) +>type : Symbol(Entity.type, Decl(narrowByBooleanComparison.ts, 72, 18)) +>ACTOR_TYPE : Symbol(ACTOR_TYPE, Decl(narrowByBooleanComparison.ts, 75, 5)) +} +function test5(bin: Entity) { +>test5 : Symbol(test5, Decl(narrowByBooleanComparison.ts, 81, 1)) +>bin : Symbol(bin, Decl(narrowByBooleanComparison.ts, 82, 15)) +>Entity : Symbol(Entity, Decl(narrowByBooleanComparison.ts, 69, 1)) + + if (isActor(bin) === false) { +>isActor : Symbol(isActor, Decl(narrowByBooleanComparison.ts, 78, 1)) +>bin : Symbol(bin, Decl(narrowByBooleanComparison.ts, 82, 15)) + + bin; +>bin : Symbol(bin, Decl(narrowByBooleanComparison.ts, 82, 15)) + + } else { + bin; +>bin : Symbol(bin, Decl(narrowByBooleanComparison.ts, 82, 15)) + } +} +function test6(bin: Entity) { +>test6 : Symbol(test6, Decl(narrowByBooleanComparison.ts, 88, 1)) +>bin : Symbol(bin, Decl(narrowByBooleanComparison.ts, 89, 15)) +>Entity : Symbol(Entity, Decl(narrowByBooleanComparison.ts, 69, 1)) + + if (isActor(bin) == false) { +>isActor : Symbol(isActor, Decl(narrowByBooleanComparison.ts, 78, 1)) +>bin : Symbol(bin, Decl(narrowByBooleanComparison.ts, 89, 15)) + + bin; +>bin : Symbol(bin, Decl(narrowByBooleanComparison.ts, 89, 15)) + + } else { + bin; +>bin : Symbol(bin, Decl(narrowByBooleanComparison.ts, 89, 15)) + } +} + +// https://github.com/microsoft/TypeScript/issues/53005 +function isFunction(x: unknown): x is Function { +>isFunction : Symbol(isFunction, Decl(narrowByBooleanComparison.ts, 95, 1)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 98, 20)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 98, 20)) +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + return typeof x === "function"; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 98, 20)) +} + +function test7(x: unknown) { +>test7 : Symbol(test7, Decl(narrowByBooleanComparison.ts, 100, 1)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 102, 15)) + + if (isFunction(x) !== false) { +>isFunction : Symbol(isFunction, Decl(narrowByBooleanComparison.ts, 95, 1)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 102, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 102, 15)) + } + if (isFunction(x) === true) { +>isFunction : Symbol(isFunction, Decl(narrowByBooleanComparison.ts, 95, 1)) +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 102, 15)) + + x; +>x : Symbol(x, Decl(narrowByBooleanComparison.ts, 102, 15)) + } +} + diff --git a/tests/baselines/reference/narrowByBooleanComparison.types b/tests/baselines/reference/narrowByBooleanComparison.types new file mode 100644 index 0000000000000..a21e39c92f7fb --- /dev/null +++ b/tests/baselines/reference/narrowByBooleanComparison.types @@ -0,0 +1,308 @@ +//// [tests/cases/compiler/narrowByBooleanComparison.ts] //// + +=== narrowByBooleanComparison.ts === +type A = { type: "A" }; +>A : { type: "A"; } +>type : "A" + +type B = { type: "B" }; +>B : { type: "B"; } +>type : "B" + +type C = { type: "C" }; +>C : { type: "C"; } +>type : "C" + +type MyUnion = A | B | C; +>MyUnion : A | B | C + +const isA = (x: MyUnion): x is A => x.type === "A"; +>isA : (x: MyUnion) => x is A +>(x: MyUnion): x is A => x.type === "A" : (x: MyUnion) => x is A +>x : MyUnion +>x.type === "A" : boolean +>x.type : "A" | "B" | "C" +>x : MyUnion +>type : "A" | "B" | "C" +>"A" : "A" + +function test1(x: MyUnion) { +>test1 : (x: MyUnion) => void +>x : MyUnion + + if (isA(x) !== true) { +>isA(x) !== true : boolean +>isA(x) : boolean +>isA : (x: MyUnion) => x is A +>x : MyUnion +>true : true + + x; +>x : B | C + } + + if (isA(x) !== false) { +>isA(x) !== false : boolean +>isA(x) : boolean +>isA : (x: MyUnion) => x is A +>x : MyUnion +>false : false + + x; +>x : A + } + + if (isA(x) === false) { +>isA(x) === false : boolean +>isA(x) : boolean +>isA : (x: MyUnion) => x is A +>x : MyUnion +>false : false + + x; +>x : B | C + } + + if (isA(x) === true) { +>isA(x) === true : boolean +>isA(x) : boolean +>isA : (x: MyUnion) => x is A +>x : MyUnion +>true : true + + x; +>x : A + } + + if (isA(x) != true) { +>isA(x) != true : boolean +>isA(x) : boolean +>isA : (x: MyUnion) => x is A +>x : MyUnion +>true : true + + x; +>x : B | C + } + + if (isA(x) == true) { +>isA(x) == true : boolean +>isA(x) : boolean +>isA : (x: MyUnion) => x is A +>x : MyUnion +>true : true + + x; +>x : A + } + + if (true !== isA(x)) { +>true !== isA(x) : boolean +>true : true +>isA(x) : boolean +>isA : (x: MyUnion) => x is A +>x : MyUnion + + x; +>x : B | C + } + + if (true === isA(x)) { +>true === isA(x) : boolean +>true : true +>isA(x) : boolean +>isA : (x: MyUnion) => x is A +>x : MyUnion + + x; +>x : A + } +} + +// https://github.com/microsoft/TypeScript/issues/53093 +function test2(x: unknown) { +>test2 : (x: unknown) => void +>x : unknown + + if (x instanceof Error === false) { +>x instanceof Error === false : boolean +>x instanceof Error : boolean +>x : unknown +>Error : ErrorConstructor +>false : false + + return; + } + x; +>x : Error +} + +// https://github.com/microsoft/TypeScript/issues/50712 +function test3(foo: unknown) { +>test3 : (foo: unknown) => void +>foo : unknown + + if (typeof foo !== 'string' && Array.isArray(foo) === false) { +>typeof foo !== 'string' && Array.isArray(foo) === false : boolean +>typeof foo !== 'string' : boolean +>typeof foo : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>foo : unknown +>'string' : "string" +>Array.isArray(foo) === false : boolean +>Array.isArray(foo) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>foo : unknown +>false : false + + throw new Error('Not a string or an array'); +>new Error('Not a string or an array') : Error +>Error : ErrorConstructor +>'Not a string or an array' : "Not a string or an array" + } + foo; +>foo : string | any[] +} + +// https://github.com/microsoft/TypeScript/issues/55395 +class WebError extends URIError { +>WebError : WebError +>URIError : URIError + + status?: number; +>status : number | undefined +} +function test4() { +>test4 : () => void + + try { + // make a request + } catch (err) { +>err : unknown + + if (err instanceof WebError === false || err.status != 401) { +>err instanceof WebError === false || err.status != 401 : boolean +>err instanceof WebError === false : boolean +>err instanceof WebError : boolean +>err : unknown +>WebError : typeof WebError +>false : false +>err.status != 401 : boolean +>err.status : number | undefined +>err : WebError +>status : number | undefined +>401 : 401 + + console.error(err); +>console.error(err) : void +>console.error : (...data: any[]) => void +>console : Console +>error : (...data: any[]) => void +>err : unknown + } + } +} + +// https://github.com/microsoft/TypeScript/issues/44366 +interface Entity { + type: string; +>type : string +} +const ACTOR_TYPE = "actor"; +>ACTOR_TYPE : "actor" +>"actor" : "actor" + +interface Actor extends Entity { + type: typeof ACTOR_TYPE; +>type : "actor" +>ACTOR_TYPE : "actor" +} +function isActor(entity: Entity): entity is Actor { +>isActor : (entity: Entity) => entity is Actor +>entity : Entity + + return entity.type === ACTOR_TYPE; +>entity.type === ACTOR_TYPE : boolean +>entity.type : string +>entity : Entity +>type : string +>ACTOR_TYPE : "actor" +} +function test5(bin: Entity) { +>test5 : (bin: Entity) => void +>bin : Entity + + if (isActor(bin) === false) { +>isActor(bin) === false : boolean +>isActor(bin) : boolean +>isActor : (entity: Entity) => entity is Actor +>bin : Entity +>false : false + + bin; +>bin : Entity + + } else { + bin; +>bin : Actor + } +} +function test6(bin: Entity) { +>test6 : (bin: Entity) => void +>bin : Entity + + if (isActor(bin) == false) { +>isActor(bin) == false : boolean +>isActor(bin) : boolean +>isActor : (entity: Entity) => entity is Actor +>bin : Entity +>false : false + + bin; +>bin : Entity + + } else { + bin; +>bin : Actor + } +} + +// https://github.com/microsoft/TypeScript/issues/53005 +function isFunction(x: unknown): x is Function { +>isFunction : (x: unknown) => x is Function +>x : unknown + + return typeof x === "function"; +>typeof x === "function" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : unknown +>"function" : "function" +} + +function test7(x: unknown) { +>test7 : (x: unknown) => void +>x : unknown + + if (isFunction(x) !== false) { +>isFunction(x) !== false : boolean +>isFunction(x) : boolean +>isFunction : (x: unknown) => x is Function +>x : unknown +>false : false + + x; +>x : Function + } + if (isFunction(x) === true) { +>isFunction(x) === true : boolean +>isFunction(x) : boolean +>isFunction : (x: unknown) => x is Function +>x : unknown +>true : true + + x; +>x : Function + } +} + diff --git a/tests/cases/compiler/narrowByBooleanComparison.ts b/tests/cases/compiler/narrowByBooleanComparison.ts new file mode 100644 index 0000000000000..6dbb34a4055a0 --- /dev/null +++ b/tests/cases/compiler/narrowByBooleanComparison.ts @@ -0,0 +1,113 @@ +// @strict: true +// @noEmit: true + +type A = { type: "A" }; +type B = { type: "B" }; +type C = { type: "C" }; +type MyUnion = A | B | C; + +const isA = (x: MyUnion): x is A => x.type === "A"; + +function test1(x: MyUnion) { + if (isA(x) !== true) { + x; + } + + if (isA(x) !== false) { + x; + } + + if (isA(x) === false) { + x; + } + + if (isA(x) === true) { + x; + } + + if (isA(x) != true) { + x; + } + + if (isA(x) == true) { + x; + } + + if (true !== isA(x)) { + x; + } + + if (true === isA(x)) { + x; + } +} + +// https://github.com/microsoft/TypeScript/issues/53093 +function test2(x: unknown) { + if (x instanceof Error === false) { + return; + } + x; +} + +// https://github.com/microsoft/TypeScript/issues/50712 +function test3(foo: unknown) { + if (typeof foo !== 'string' && Array.isArray(foo) === false) { + throw new Error('Not a string or an array'); + } + foo; +} + +// https://github.com/microsoft/TypeScript/issues/55395 +class WebError extends URIError { + status?: number; +} +function test4() { + try { + // make a request + } catch (err) { + if (err instanceof WebError === false || err.status != 401) { + console.error(err); + } + } +} + +// https://github.com/microsoft/TypeScript/issues/44366 +interface Entity { + type: string; +} +const ACTOR_TYPE = "actor"; +interface Actor extends Entity { + type: typeof ACTOR_TYPE; +} +function isActor(entity: Entity): entity is Actor { + return entity.type === ACTOR_TYPE; +} +function test5(bin: Entity) { + if (isActor(bin) === false) { + bin; + } else { + bin; + } +} +function test6(bin: Entity) { + if (isActor(bin) == false) { + bin; + } else { + bin; + } +} + +// https://github.com/microsoft/TypeScript/issues/53005 +function isFunction(x: unknown): x is Function { + return typeof x === "function"; +} + +function test7(x: unknown) { + if (isFunction(x) !== false) { + x; + } + if (isFunction(x) === true) { + x; + } +}