Skip to content

keyof with union types generates wrong list of keys. #28962

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

Closed
JohannesKlauss opened this issue Dec 11, 2018 · 3 comments
Closed

keyof with union types generates wrong list of keys. #28962

JohannesKlauss opened this issue Dec 11, 2018 · 3 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@JohannesKlauss
Copy link

JohannesKlauss commented Dec 11, 2018

TypeScript Version: 3.3.0-dev.20181211

keyof union

Code

interface A {
    propA: string;
}

interface K {
    propK: string;
}

interface L {
    propL: string;
}

type CombinedOr = K | L;

type CombinedAnd = A & CombinedOr;

type Fields = keyof CombinedAnd;

const foo: Fields = "propK";

Expected behavior:
"propK" is a valid assignment to foo: Fields. Since CombinedOr can hold K or L I expect the compiler to generate multiple correct assignments to foo. The first option would be to hold propA or propK and the second option would be to hold propA or propL.

Actual behavior:
error TS2322: Type '"propK"' is not assignable to type '"propA"'.

Playground Link: http://www.typescriptlang.org/play/#src=interface%20A%20%7B%0D%0A%20%20%20%20propA%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20B%20%7B%0D%0A%20%20%20%20propB%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20C%20%7B%0D%0A%20%20%20%20propC%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20K%20%7B%0D%0A%20%20%20%20propK%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20L%20%7B%0D%0A%20%20%20%20propL%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20M%20%7B%0D%0A%20%20%20%20propM%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Atype%20CombinedOr%20%3D%20K%20%7C%20L%20%7C%20M%3B%0D%0A%0D%0Atype%20CombinedAnd%20%3D%20A%20%26%20B%20%26%20C%20%26%20CombinedOr%3B%0D%0A%0D%0Atype%20Fields%20%3D%20keyof%20CombinedAnd%3B%0D%0A%0D%0Aconst%20foo%3A%20Fields%20%3D%20%22propK%22%3B

Related Issues:
I don't know if this already exists. I haven't found any.

@ahejlsberg
Copy link
Member

This is working as intended. When applied to a union type, the keyof type operator produces a union of the keys that are present in all of the constituents of the union. In other words, a key type that is safe to apply to every possible shape of the union. In your example, only "propA" is known to be a valid key for CombinedAnd.

@ahejlsberg ahejlsberg added the Working as Intended The behavior described is the intended behavior; this is not a bug label Dec 11, 2018
@vcarl
Copy link

vcarl commented Dec 11, 2018

Somebody provided this link to a talk you gave where you discuss exactly this, so cheers. I wonder though if this slightly different demo should result in a never type, though? If there are no keys in common, it seems to produce a type that can't be satisfied.

@ahejlsberg
Copy link
Member

@vcarl What you're seeing there is that we defer elimination of vacuous intersection types until they are put into a union type. The Fields type in your example is "propK" & "propL". For assignment purposes this type behaves as never, but we keep it around to allow you to better diagnose where the never-like type came from. However, the minute you union it with something we remove it. For example:

type X = "propK" & "propL";
type Y = X | string;  // Reduced to just string

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

3 participants