Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge type does not handle correctly optional properties #402

Closed
adridavid opened this issue Jun 1, 2022 · 2 comments · Fixed by #427
Closed

Merge type does not handle correctly optional properties #402

adridavid opened this issue Jun 1, 2022 · 2 comments · Fixed by #427

Comments

@adridavid
Copy link
Contributor

The problem happens when Merge is used with two types that share one same key, and when the key is optional in the second type. The returned type for this property should be a union of the two initial types.

Example

Given these two interfaces :

interface Foo {
    baz?: "a";
    qux: number
}

interface Bar {
    baz?: "b",
    qux: string
}

If we define two values typed as Foo and Bar :

const foo: Foo = {
    baz: "a",
    qux: 2
}

const bar: Bar = {
    qux: "qux"
}

After merging these two objects, the inferred type is :

const result = {
    ...foo,
    ...bar
}
// const result: {
//    baz?: "b" | "a" | undefined;
//    qux: string;
// }

The optional property baz is typed as "b" | "a" | undefined.

Using Merge, the optional property baz is typed as "b" | undefined :

type Result = Merge<Foo, Bar>
// type Result = {
//    baz?: "b" | undefined;
//    qux: string;
// }

"a" is missing in the union here.

I don't know if Merge should be changed to handle this use case or if a new type should be created ?

@skarab42
Copy link
Collaborator

skarab42 commented Jul 1, 2022

It's very strange, I don't expect that when I do an intersection. And TypeScript also seems to handles the case differently depending on whether you use the spread operator or an Object.assign. REPL

const result2 = Object.assign(Object.assign({}, foo), bar); // infered as "Foo & Bar"

type FooBar = Foo & Bar;

const result3:FooBar = { // <- ERROR Type '"b"' is not assignable to type '"a"'
    ...foo,
    ...bar
}

@skarab42
Copy link
Collaborator

skarab42 commented Jul 1, 2022

In any case when I do a merge I expect the destination value to be replaced if the source exists. Maybe we need another type to handle this ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants