Skip to content

Union types (Foo | Bar) inconsistently becomes Partial<Foo & Bar> Β #59819

Closed
@davidbarratt

Description

@davidbarratt

πŸ”Ž Search Terms

"union type partial", "type union narrowing"

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about Unions

⏯ Playground Link

https://www.typescriptlang.org/play/?exactOptionalPropertyTypes=true&ts=5.5.4#code/JYOwLgpgTgZghgYwgAgGIHt3IN4ChkHIyYBcyAzmFKAOYDcuAvrrqJLIigEJxQ76EARrzIgArgFtB0BoWQAvOPLKVqIekxYwxIBGGDoQyYfIAUAByjpz5MhiwAfZDz4BKfnOAxkpgETCoX2RQZEtrcnc8OTkEQ3J0ABsIADoE9BoLKxtkxXlXWUJGZAgE8hQo6IJYkHik1PTM8OTidHyBAmZmXBNTbCJSZF9ISiDGfOQAegnkYbBupV7jEWQARgAmAGYAFgAaBSUyXwBPMQALUfGp5BPTlivMGwBCa-QxZAQ4I3M4cnJkOAoYkEZTAyHQ3n86DA52QUDg0OgM1On0GhhQ6D4CLBCMCyRYPT6LUOs18ewCZHWG2QYzok2mOgAJhAYKAIAz5mZCQMhhARntcocGeQYAyYCsLrSrrMgA

πŸ’» Code

interface Foo {
    foo: string;
}

interface Bar {
    bar: number;
    zaz: string;
}

function baz(props: Foo | Bar ) {
    if ("bar" in props) {
        console.log(props.zaz);
    } else {
        console.log(props.foo);
    }
}

baz({ foo: "test" }); // test
baz({ bar: 1234, zaz: "yuh" }); // yuh

// oops! you can pass a subset of "both" rather than "one or the other".

baz({ foo: "test", bar: 123 }); // undefined
baz({ foo: "test", zaz: "dsfdf1" }); // test

πŸ™ Actual behavior

I was surprised that I would pass in an object that is neither Foo nor Bar and TypeScript didn't complain about. This caused a bug at runtime that it seems like TypeScript should have caught?

Interestingly, the function definition is well aware that only one or the other could be passed. I could not write a scenario where that wasn't the case, but calling the funciton is where this fails.

πŸ™‚ Expected behavior

I expected to be able to pass Foo or Bar but not both and certainly not neither.

Additional information about the issue

I couldn't find a previous version of TypeScript where this worked as I would expect it too. I was really wondering if I was doing something wrong or this was a well known issue, but I was struggling to find anything on the topic.

It looks like this also fails:

const data: Foo|Bar = {
    foo: "test",
    bar: 123,
};

in which case data is neither Foo nor Bar but TypeScript doesn't complain about it.

You can see this with both of these that fail:

const data: Foo = {
    foo: "test",
    bar: 123,
};
const data: Bar = {
    foo: "test",
    bar: 123,
};

I would think that if they fail seperately, a union should also fail?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions