Skip to content

Allow direct property check if (x.a) to work and narrow type if at least one type in typeof x has a #38156

Closed
@ashmind

Description

@ashmind

Search Terms

condition property if in guard

Suggestion

I would like the following to work:

function f(x: { a: string }|{ b: string }) {
    if (x.a) {
        // x should be { a: string } here
    }
}

Use Cases

This is very common pattern in JavaScript, so when rewriting some of my JS code to TypeScript I often have to change this.

E.g.

function logError(e: Error|TaskError) {
    if (e.fromTask) {
        // TaskError, log fromTask
    }
    else {
        // other error, simple log
    }
    ...
}

I can rewrite this in two ways.

First

if ('fromTask' in e)

This has two issues:

  1. It does not check that at least one of union types has fromTask, so if I made a typo (fromTsk) it would not be found.
  2. It looks unnatural -- there is probably some JS pattern that recommends this over a shorter check, but I haven't seen it used much

Second

function logError(e: (Error & { fromTask?: undefined })|TaskError)

This works, but feels really artificial -- it does not give any information that previous signature didn't have, and the only purpose is to work around the if error.

Examples

Union

interface I1 { a: string }
interface I2 { b: string }
interface I3 { a?: string; b: string }

function f(x: I1|I2|I3) {
    if (x.a) {
        // type of x should be I1|(I3 & { a: string })
    }
}

No such property

interface I1 { a: string }
interface I2 { b: string }

function f(x: I1|I2) {
    if (x.c) { // should fail to compile as `c` is not present on either I1 or I2
    }
}

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions