microsoft / microsoft/TypeScript

discriminated union type matching behaviour changed starting from 5.2.x

Open
#57,231 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Domain: check: Control Flow Help Wanted Possible Improvement
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

🔎 Search Terms

"key order when matching object types" "discriminated unions key order"

🕗 Version & Regression Information
  • This changed between versions 5.1 and 5.2
⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.2.2#code/FAFwngDgpgBAYgewQExgXhgcgIYQgGykxgB8sEAnbAOwHMiBuUSWANSnpGwCND0sAzhACW1bAGMAFsTKZx2ChQQhMTcNBgB5EJKgV+mALbD8AaxlYA7thB7VzDQGEArgJAJDB7MIoXMAhBN7B1gYABUFTnRgGFi4sgBvGLiU2OxnZGEoanEoAC4sKAUde1TUwyL8POSylLIXNw8a2tiyAAMAMwpnYRAAfQASBMQUAF825pb2gDcOKC5eKEGE9k4eQnHJ2vblXQpl7T3xphbRrcStlPTM7NyCowUBPwArZxFbXxOWmArsKpg3BRRLQvmUzsBgOIENQ3DBZmtFgVVvN1rAMHIFEoVExIdDYVwKJwChFCfN+DAklcMlkcvlCsVpAAaZq-f5teEoxbLDkLDYTUY4qEwkAwAmcABMxMiZIwFJZlQK7LmvKWQx5qPGzKpN1p9yKFBKwAFwCAA

💻 Code

type Food = 'apple' | 'orange';
type Vegetable = 'spinach' | 'carrot';
type Other = 'milk' | 'water';
type Custom = 'air' | 'soil';

type  Target =
      | {
          audience: 'earth';
          meal:
            | Custom
            | `fruit_${Food}`
            | `vegetable_${Vegetable}`
            | `other_${Other}`;
        }
      | {
          audience: 'mars' | 'jupiter';
          meal: string;
        }


const vegetable: Vegetable = 'carrot';

// ok
const target: Target =  {
    audience: 'earth',
    meal: `vegetable_${vegetable}`
};

// TS Error
// Type '{ meal: string; audience: "earth"; }' is not assignable to type 'Target'.
//  Types of property 'audience' are incompatible.
//    Type '"earth"' is not assignable to type '"mars" | "jupiter"'
const target2: Target =  {
    meal: `vegetable_${vegetable}`,
    audience: 'earth'
};

Output
"use strict";
const vegetable = 'carrot';

const target = { // ok
    audience: 'earth',
    meal: `vegetable_${vegetable}`
};

const target2 = { // error
    meal: `vegetable_${vegetable}`,
    audience: 'earth'
};

Compiler Options
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "target": "ES2017",
    "jsx": "react",
    "module": "ESNext",
    "moduleResolution": "node"
  }
}

Playground Link: Provided

🙁 Actual behavior

In the code both target and target2 have the correct structure based on the Target type.

But the order of keys is reversed in target2 which used to work but not anymore.

🙂 Expected behavior

Both target and target2 should be valid.

Additional information about the issue

Discriminated unions didn't rely on the key ordering AFAIK.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the provided Playground reproduction and compare its behavior in TypeScript 5.1 and 5.2.2. Confirm whether both property orders are accepted, then trace the discriminated-union matching behavior and add a regression test if the relevant test location can be identified. Done means the reversed-key example type-checks without weakening the other diagnostics.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.