Skip to content

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

Closed

Description

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.

Activity

  1. MartinJohns commented on Nov 20, 2019

    @MartinJohns
    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.

  2. jack-williams commented on Nov 20, 2019

    @jack-williams
    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.

  3. mc-suchecki commented on Nov 25, 2019

    @mc-suchecki
    Author

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

  4. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions