Repository navigation
Extract breaks when upgrading to TypeScript 5.1 #54680
Copy link
Copy link
Closed
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Description
Activity
Bisects to #53098.
Andarist commented
on Jun 17, 2023 ContributorMore actionsAs a workaround you can use this:
export function handle<M extends Message>(callbacks: { - [K in M["type"]]: (msg: Extract<M, { type: K }>["value"]) => unknown; + [K in M["type"]]: (msg: (M & { type: K })["value"]) => unknown; }) { window.addEventListener("message", (event) => { const msg = event.data as M; callbacks[msg.type as keyof typeof callbacks](msg.value); }); }Reacted by Yuri Sulyma- addedNeeds InvestigationThis issue needs a team member to investigate its status.This issue needs a team member to investigate its status.
on Jun 19, 2023 I think the new behavior is defensible. The old behavior was too permissive and would for example allow
callbacks[msg.type as keyof typeof callbacks](42); callbacks[msg.type as keyof typeof callbacks]("hello");
even though one or the other must be wrong.
I think this is an instance of the pattern that was addressed in #47109. I'd suggest rewriting the example to
type MessageMap = { A: string, B: number } type Message<K extends keyof MessageMap> = { type: K, value: MessageMap[K] }; type Callbacks<K extends keyof MessageMap> = { [P in K]: (value: MessageMap[P]) => unknown }; export function handle<K extends keyof MessageMap>(callbacks: Callbacks<K>) { window.addEventListener("message", (event) => { const msg = event.data as Message<K>; callbacks[msg.type](msg.value); }); }
- addedWorking as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bugand removedNeeds InvestigationThis issue needs a team member to investigate its status.This issue needs a team member to investigate its status.
on Jun 26, 2023 Andarist commented
on Jun 26, 2023 ContributorMore actionsIt kinda means that the workaround that I suggested here is broken, right? I mean, if this
Extract-based solution is considered to be flawed (I understand the argument behind it) then the intersection variant should really be treated the same way.typescript-bot commented
on Jun 28, 2023 ContributorMore actionsThis issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
- locked as resolved and limited conversation to collaborators
on Oct 22, 2025
Metadata
Metadata
Assignees
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Bug Report
Some of my code using
Extractstopped working when I upgraded from 5.0 to 5.1. This may be related to #54676. I didn't see anything in the release announcement to suggest that this might happen.🔎 Search Terms
Extract, TypeScript 5.1, discriminated union
🕗 Version & Regression Information
⏯ Playground Link
https://typescript-eslint.io/play/#ts=5.1.3&sourceType=module&code=C4TwDgpgBAglC8UDeAoKVSQFxQEQ1wG40oA3AQwBsBXCHAZ2ACcBLAOwHNiBfYlTaACEEyEgJy5BREhRp0obagFsARhCY8+AqAFkI9euQ7REcAD5RBfCAA8wAeybAoAM2psAxsBb22UABbkbAAmlBAAPDpQtsAQIfS6+obGAHwAFB5UlCrkHgDW9Dio6ADaANJQ7LoluAK4ALr1OGlK9Bw4AKI2zLnAkQA0yBjg8hXcKTWytA0AlAgpUO55bPYA7mw8c8VQq+zBawB05MHBHaRxwAAyLIxx6mm4SklGELiDaRDnbMBz8Avb6A8vkYUFaHBEnwuB2C5GA5Cg5ASOmI6EBWRy+XoJTBB20iKgeQgIHsLmGkBJUEylGyuQK9RabQOUwgMxRUG4rJQ3BQQA&eslintrc=N4KABGBEBOCuA2BTAzpAXGYBfEWg&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eAcgK6qoDCAFutAGsylBm3TgwAXxCSgA
💻 Code
🙁 Actual behavior
In TypeScript 5.0, this code works. In TypeScript 5.1, it complains about
msg.value, with the error🙂 Expected behavior
Code should work as it did in 5.0.4. If this is now intended behavior, I would like to know how to fix my code.