Skip to content

Union type two interfaces (non-primitive unions) #23142

@TrySpace

Description

@TrySpace

I doubt this is expected behaviour, but it might be that I'm missing something.

Code
Doing this:

interface One {
  one: string;
}

interface Two {
  two: string;
}

type Three = One | Two;

const what1: Three = { one: 'ok' };
const what2: Three = { two: 'ok' };

const good1 = what1.one;
const good2 = what2.two;

good1 & good2 are good.

But adding a property to One:

interface One {
  one: string;
  extra: number; // <-- New line
}

interface Two {
  two: string;
}

type Three = One | Two;

const what1: Three = { one: 'ok' }; // Error
const what2: Three = { two: 'ok' };

const good1 = what1.one; // Error
const good2 = what2.two;

Typescript will warn about both what1 and what1.one, respectively:

 TS2322: Type '{ one: string; }' is not assignable to type 'Three'.
  Type '{ one: string; }' is not assignable to type 'Two'.
    Property 'two' is missing in type '{ one: string; }'.

So instead of error for missing extra prop, it will error for it missing a type from a seemingly arbitrary type.

and

TS2339: Property 'one' does not exist on type 'Three'.
  Property 'one' does not exist on type 'Two'.

Here it looks like ts has coerced/figured out the type already.

Playground Link:
Just uncomment the line here

Trying to align them doesn't help:
When I add the extra property to the second type

What fixes these issues is when I change it into an optional extra?: string: here

Expected behavior:
I would have expected it to behave like logical or, but adding new properties seems to break the union.
Invalid, but maybe something like this: type Three = One? | Two?; is what I actually expected?
I expected no error.

Actual behavior:
The union seems to behave as &.

If I do foo: string | number, I expect foo to accept either type.
My guess is that with non-primitive types it doesn't work like that, since I couldn't literally merge an object with a boolean, so it merges the two interfaces, but somehow both are being reverse hypocritical, as in either are complaining about something the other is missing, while they have it themselves. And the order doesn't matter either, so they will always be incompatible whatever you try.
But that doesn't explain why it would work without the extra property, but different properties.

Related Issues:
#23031 The title seems to match my issue, but the errors are different. Although I have to say I've had that error before in relation to using two interfaces in union.

Search Terms:
type interfaces union

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