Skip to content

Logical and loses narrowed types when testing for truthyness on union types #29106

Closed
@asmundg

Description

@asmundg

TypeScript Version: 3.3.0-dev.20181220

Search Terms: narrow union regression

Code

const f = (_a: string, _b: string): void => {};

interface A {
  a?: string;
  b?: string;
}

interface B {
  a: string;
  b: string;
}

type U = A | B;

const u: U = {} as any;

// u.b narrowed, but not a
// Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
//   Type 'undefined' is not assignable to type 'string'.
u.a && u.b && f(u.a, u.b);

// Error: u.a narrowed, but not b
// Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
//   Type 'undefined' is not assignable to type 'string'.
u.b && u.a && f(u.a, u.b);

// Works
typeof u.a === "string" && typeof u.b === "string" && f(u.a, u.b);

// Works
const a: A = {} as any;
a.b && a.a && f(a.a, a.b);

Expected behavior:

All forms narrow correctly and compile with strictNullChecks.

This works correctly in 3.1.6

Actual behavior:

Only non-union or explicitly checking typeof narrows correctly in >=3.2.

Playground Link: (Needs strictNullChecks enabled) http://www.typescriptlang.org/play/#src=const%20f%20%3D%20(_a%3A%20string%2C%20_b%3A%20string)%3A%20void%20%3D%3E%20%7B%7D%3B%0D%0A%0D%0Ainterface%20A%20%7B%0D%0A%20%20a%3F%3A%20string%3B%0D%0A%20%20b%3F%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20B%20%7B%0D%0A%20%20a%3A%20string%3B%0D%0A%20%20b%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Atype%20U%20%3D%20A%20%7C%20B%3B%0D%0A%0D%0Aconst%20u%3A%20U%20%3D%20%7B%7D%20as%20any%3B%0D%0A%0D%0A%2F%2F%20u.b%20narrowed%2C%20but%20not%20a%0D%0A%2F%2F%20Argument%20of%20type%20'string%20%7C%20undefined'%20is%20not%20assignable%20to%20parameter%20of%20type%20'string'.%0D%0A%2F%2F%20%20%20Type%20'undefined'%20is%20not%20assignable%20to%20type%20'string'.%0D%0Au.a%20%26%26%20u.b%20%26%26%20f(u.a%2C%20u.b)%3B%0D%0A%0D%0A%2F%2F%20Error%3A%20u.a%20narrowed%2C%20but%20not%20b%0D%0A%2F%2F%20Argument%20of%20type%20'string%20%7C%20undefined'%20is%20not%20assignable%20to%20parameter%20of%20type%20'string'.%0D%0A%2F%2F%20%20%20Type%20'undefined'%20is%20not%20assignable%20to%20type%20'string'.%0D%0Au.b%20%26%26%20u.a%20%26%26%20f(u.a%2C%20u.b)%3B%0D%0A%0D%0A%2F%2F%20Works%0D%0Atypeof%20u.a%20%3D%3D%3D%20%22string%22%20%26%26%20typeof%20u.b%20%3D%3D%3D%20%22string%22%20%26%26%20f(u.a%2C%20u.b)%3B%0D%0A%0D%0A%2F%2F%20Works%0D%0Aconst%20a%3A%20A%20%3D%20%7B%7D%20as%20any%3B%0D%0Aa.b%20%26%26%20a.a%20%26%26%20f(a.a%2C%20a.b)%3B%0D%0A

Related Issues:

#29012

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScript

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions