Skip to content

Type checking of discriminated nullable union depends on type orderΒ #62512

@maxpatiiuk

Description

@maxpatiiuk

πŸ”Ž Search Terms

10 11 union undefined type getKeyPropertyName discriminated

πŸ•— Version & Regression Information

  • This changed between versions 4.2.3 and 4.3.5 (before it would complain about undefined in both cases)

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.2#code/JYOwLgpgTgZghgYwgAgILIN4Chm+QZwFcAjAFQE8AHCALmQCJj6BuHPMK2huFrAXywATCAgA2cKClEQwyAPZRhUACIRqIYeBpsAPpmQdqdegEZ6yAbj2pd+w13oAmc5eR6MBzsYDML2x-tjABY-KzsvBgBWULdwowYANhj3T3j6AHZkuIcADiyAiPoATnzUhxMABizCDQgYUAhBVmAYAAoFJVV1TTAAQgA6e2QAXlHuegBKbDxkAHpZ5AByDuguiFrwReRgfGRKOXx8YGJRciWa4XqQRsX+1pMciqCcibZcBDkQfDlpftE5ADm7UUqzU6x6g04E1YAiEInEkmQ0lkKxUYI2YEc2jCBTSZgstlQjn8ZWMzgJONJDF8FNiuIcIVpKUCUVKLPoSSZ2WMmS59OMeT5VOKbMKlWqtSujWa4Gg8CQaEcmDeBBIFDSTFYM3ZPBhWBawM66J6jgGQ1Gw3GUxV82QADk5MhoFAFMhWh8oJIELJiBAABZwABuwAUAH5XjMPl8fhA-oDDaDuutMZDqND+EA

πŸ’» Code

interface A {
    subType: "b";
    type: "a";
}
declare let orderDependent:
  | { type: "1" }
  | A
  | { type: "2" }
  | { type: "3" }
  | { type: "4" }
  | { type: "5" }
  | { type: "6" }
  | { type: "7" }
  | { type: "8" }
  | { type: "9" }
  | { type: "10" }
  | undefined;
if(orderDependent!.type === "a"){
    // 'orderDependent' is possibly 'undefined'.(18048)
    console.log(orderDependent.type);
}

declare let orderDependent2:
  | { type: "1" }
  | A2
  | { type: "2" }
  | { type: "3" }
  | { type: "4" }
  | { type: "5" }
  | { type: "6" }
  | { type: "7" }
  | { type: "8" }
  | { type: "9" }
  | { type: "10" }
  | undefined;
interface A2 {
    subType: "b";
    type: "a";
}
if(orderDependent2!.type === "a"){
    // No error (correct behavior?)
    console.log(orderDependent2.type);
}

πŸ™ Actual behavior

TypeScript may emit a possibly 'undefined' error depending on the order in which types are defined.
Whether that error should be there in the first place is another question (#62511)

πŸ™‚ Expected behavior

Type order should not impact type checking

Additional information about the issue

Cause investigation

Relevant function in checker.ts: getKeyPropertyName()

That function tries to find the "discriminated union key" and index the type union by it.

The logic is quite simple: find the first property of literal type among any union members (starting from the first) and try to index by it. If that property successfully discriminated the union, use it as an index. Otherwise fallback to slower check.

The union members are sorted by typeId, which is assigned sequentially in order in which the types are created.

In the linked example, the true discriminator is type. However if first union member has another literal type property appear earlier (subType), it is falsely selected as index, and indexing fails.

Related issue: #62511

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