Skip to content

Property ... is missing in type ... but required in type ... when uncertainty is introduced during assignment to variable of discriminated union type #54676

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
FelixZY opened this issue Jun 16, 2023 Β· 3 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@FelixZY
Copy link

FelixZY commented Jun 16, 2023

Bug Report

πŸ”Ž Search Terms

No idea how to search for this.

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about nothing

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type A =
  | {
      prop: {
        type: "number";
        num: number;
        str?: undefined;
      };
    }
  | {
      prop: {
        type: "string";
        num?: undefined;
        str: string;
      };
    };

// Expected: No error
// Actual: Property 'str' is missing in type '{ type: "number"; num: number; }' but required in type '{ type: "string"; num?: undefined; str: string; }'.
const a: A = {
  prop:
    // Introducing uncertainty in the assignment
    // appears to be key to triggering this bug
    Math.random() > 0.5
      ? {
          type: "string",
          str: "hello",
        }
      : {
          type: "number",
          num: 123,
        },
};

πŸ™ Actual behavior

Type '{ prop: { type: "string"; str: string; } | { type: "number"; num: number; }; }' is not assignable to type 'A'.
  Type '{ prop: { type: "string"; str: string; } | { type: "number"; num: number; }; }' is not assignable to type '{ prop: { type: "string"; num?: undefined; str: string; }; }'.
    Types of property 'prop' are incompatible.
      Type '{ type: "string"; str: string; } | { type: "number"; num: number; }' is not assignable to type '{ type: "string"; num?: undefined; str: string; }'.
        Property 'str' is missing in type '{ type: "number"; num: number; }' but required in type '{ type: "string"; num?: undefined; str: string; }'.

πŸ™‚ Expected behavior

No error

@fatcerberus
Copy link

fatcerberus commented Jun 16, 2023 β€’

In general, { foo: A | B } is not assignable to { foo: A } | { foo: B } (there are some exceptions, but that's the general rule). This happens to be safe with one property, but not when there are multiple. Since the uncertainty is specifically in the value of prop and not the entire object, the structure of the type should reflect that.

@FelixZY
Copy link
Author

FelixZY commented Jun 16, 2023 β€’

I see. For this particular case I wanted very specifically to have a single property and to do

type ABase = {
  // ...
}

export type AString = ABase & {
  prop: {
    type: "string";
    num?: undefined;
    str: string;
  };
}

export type ANumber = ABase & {
  prop: {
    type: "number";
    num: number;
    str?: undefined;
  };
}

export type A = AString | ANumber;

I guess this could be done with even more type variables or Extract instead but I find the lack of intellisense (vscode) on the second type parameter makes Extract a bit unfriendly/unsafe to use.


EDIT:

Solved this by doing:

type AStringProp = {
  prop: {
    type: "string";
    num?: undefined;
    str: string;
  };
}

type ANumberProp = {
  prop: {
    type: "number";
    num: number;
    str?: undefined;
  };
}

type AProp = AStringProp | ANumberProp;

export type A = {
  // ...
  prop: AProp,
}
export type AString = A & { prop: AStringProp };
export type ANumber = A & { prop: ANumberProp };

@fatcerberus
Copy link

You could write it like this:

type ABase = {
  // ...
}

export type AStringProp = {
    type: "string";
    num?: undefined;
    str: string;
};

export type ANumberProp = {
    type: "number";
    num: number;
    str?: undefined;
};

export type A = ABase & {
  prop: 
    | AStringProp
    | ANumberProp
};

@DanielRosenwasser DanielRosenwasser added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Jun 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants