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

Union of types contains only the keys that exist in both subtypes #35224

Closed
mc-suchecki opened this issue Nov 20, 2019 · 3 comments
Closed

Union of types contains only the keys that exist in both subtypes #35224

mc-suchecki opened this issue Nov 20, 2019 · 3 comments

Comments

@mc-suchecki
Copy link

I see some inconsistency in the type system regarding union types. In my opinion the current implementation is quite confusing. Please see the example below.

TypeScript 3.7.2
Playground link

Input:

/* EXAMPLE 1 - keyof accepts optional fields */
interface Type {
    requiredField: string;
    optionalField?: string;
}

const test = (key: keyof Type) => {};

test('requiredField'); // keyof accepts required fields (of course)
test('optionalField'); // but also optional fields (makes sense)

/* EXAMPLE 2 - keyof does not accept optional fields from union type */

interface Subtype1 {
    commonField: string;
}

interface Subtype2 {
    commonField: string;
    uniqueField: string;
}

type Supertype = Subtype1 | Subtype2;

const test2 = (key: keyof Supertype) => {};

test2('commonField'); // this is accepted (like line 9)
test2('uniqueField'); // then why this is not accepted (like line 10)?

Output:
This line fails:

test2('uniqueField');

Expected behavior:
This line compiles fine:

test2('uniqueField');

In my understanding, Supertype type defined above should evaluate to something like:

interface Supertype {
  commonField: string;
  uniqueField?: string;
}

Whereas, from the playground example, we can see that it evaluates to:

interface Supertype {
  commonField: string;
}

I would not expect that. Can you please explain the reasoning behind such behavior? Sorry for the trouble, I imagine that is a design choice, but I did not find any source on why it is implemented like so.

@MartinJohns
Copy link
Contributor

Here's a relevant StackOverflow answer regarding union types. With a union type you only get access to what is known to be present in both types. uniqueField is not present in Subtype1.

In my understanding, Supertype type defined above should evaluate to something like:

interface Supertype {
    commonField: string;
    uniqueField?: string
}

But that would be wrong. For example if you have this interface:

interface SubSubtype1 extends Subtype1 {
  uniqueField: boolean;
}

This type is compatible to Subtype1, and as a result to Subtype1 | Subtype2, but it is not compatible to the Supertype I quoted from you.

@jack-williams
Copy link
Collaborator

In my understanding, Supertype type defined above should evaluate to something like:

interface Supertype {
  commonField: string;
  uniqueField?: string;
}

This is only valid when there is a one property delta, but it doesn't work generally.

interface Subtype1 {
    commonField: string;
}

interface Subtype2 {
    commonField: string;
    uniqueField1: string;
    uniqueField2: string;
}

interface Supertype {
  commonField: string;
  uniqueField1?: string;
  uniqueField2?: string;
}

SuperType is not equivalent to SubType1 | SubType2 because the former can have uniqueField1 but not uniqueField2, while the latter either has both or none.

@mc-suchecki
Copy link
Author

Thank you for your answers! That clarifies the problem for me, I will close the issue.

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

No branches or pull requests

3 participants