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

Type is cannot satisfy A and cannot satisfy B but satisfies A | B #45339

Closed
jisaacks opened this issue Aug 5, 2021 · 6 comments
Closed

Type is cannot satisfy A and cannot satisfy B but satisfies A | B #45339

jisaacks opened this issue Aug 5, 2021 · 6 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@jisaacks
Copy link

jisaacks commented Aug 5, 2021

πŸ•— Version & Regression Information

v4.3.5

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type A = Partial<{ 
  foo: number
  bar: string
}>

type B = Partial<{
  foo: number
  other: string
}>

type C = A & B & { $inherit: "b" }

const a: A = {
  foo: 3,
  other: 'cool', //'other' does not exist in type
}

const b: C = { // $inherit is missing
  foo: 3,
  other: 'cool',
}

const c: A | C = { // OK ????
  foo: 3,
  other: 'cool',
}

πŸ™ Actual behavior

c is OK.

πŸ™‚ Expected behavior

If the same structure cannot satisfy A and cannot satisfy B it should be logical to assumed that it cannot satisfy A | B but here it does.

I would expect that since I have included other it cannot be A and since I have not included $inherit it cannot be C so I would expect this to fail. I would expect that I could only make const c ok by either removing other: cool or by adding $inherit: "b"

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Aug 5, 2021
@RyanCavanaugh
Copy link
Member

Excess property checking (other in a) is a heuristic that tries to, without false positives, identify properties that "don't belong there". It is not a facet of the type system per se -- excess properties are OK in the subtype relationship. As such, it sometimes will issue fewer errors in the presence of different hints.

What we found in real-world code is that there were many cases where people wrote unions that didn't capture the full extent of allowed properties, so excess property checking allows excess properties as long as a matching property name is found anywhere in the target union.

@jisaacks
Copy link
Author

jisaacks commented Aug 5, 2021

@RyanCavanaugh this seems weird. I mean That is how I would expect Partial<A & C> to work. Not how I would expect A | C to work.

It just seems illogical that by design X != A, X != C, X == A | C

So if this is by design, then is there a way to actually achieve this, so that a type MUST satisfy one of the union types NOT a mishmash of parts of different types in the union?

@RyanCavanaugh
Copy link
Member

So if this is by design, then is there a way to actually achieve this, so that a type MUST satisfy one of the union types NOT a mishmash of parts of different types in the union?

const c: A | C = { // OK!
  foo: 3,
  other: 'cool',
}

The initialization here is a valid A; you could equally write

const d = {
  foo: 3,
  other: 'cool',
};
const c: A | C = d;

You might be looking for #12936 or #14094, depending

@jisaacks
Copy link
Author

jisaacks commented Aug 5, 2021

@RyanCavanaugh but that is bypassing the object literal.

I could also do:

const d = {
  foo: 3,
  other: 'cool',
};
const a: A = d;

even though I cannot do:

const a: A = {
  foo: 3,
  other: 'cool', //'other' does not exist in type
}

I was under the impression that extra properties were OK when using a variable but NOT OK when using an object literal.

So it seems like you can bypass the object literal restriction on excess properties as long as the type is a union?

@MartinJohns
Copy link
Contributor

So it seems like you can bypass the object literal restriction on excess properties as long as the type is a union?

See #20863.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants