microsoft / microsoft/TypeScript

Branded literal type widened in unexpected way

オープン
#61,093 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Bug Domain: Intersection Help Wanted
主要言語
Go
スター
111k
フォーク
14.3k
平均マージ
2日 4時間
マージ済み PR(30日)
132

説明

🔎 Search Terms

literal intersection branded string

🕗 Version & Regression Information
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about branded strings/literal intersections.
⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.7.3#code/CYUwxgNghgTiAEYD2A7AzgF3hgngBxAGkQcAueAVxQEsBHChNHAWwCMkIBuAKG9wPgAVfCACSAEQA8wgQF5KKANYokAdxQAaeBPggAHhhApgaeJhjUUAc3jzzlqwD5b28fABk8AN7wA2vyISAF0AfnIZBABfHm4AMyowDGpUbBEJaREtHX1DY1N7a0cACmpgcgkASnC0qQis8Wcvbnh4OAwKGBR4Up5I3gD4YhwAeVjpbIMjEyEa53lBCdzpiPSoFBwtS1iQGFdnENd4chQQADcdmIGI0fG3HKnTFYaXBbvJvJmCdK2dz5B9v5HeAnc4wS4ieAABRgSFOpRAaEkkN072WsxcPjgUGAqAgOD8gm6XSGN0hjiC1QIpOc0V4AHo6fAALQssAUDAspm8SyGGCxKBgBAAMSQSG8zRa8FioqKVXgpyQPW4fW4yHQWBFYvkAQkRQARJq9RUzFAkmhYtQEX90prHDwGZL4AA9EK8NWYeAAIVg5GhsPhiICSFi8FtGIlLV8mop4sdjulSFl3j6jr60SAA

💻 Code
declare const typeKey: unique symbol;

type TypeID<Type = unknown, ID extends string = string> = ID & { [typeKey]?: Type };

function typeID<Type, ID extends string>(id: ID): TypeID<Type, ID> {
  return id;
}

type KeyOf<TID extends TypeID> = TID extends TypeID<any, infer ID> ? ID : never;

type TypeOf<TID extends TypeID> = TID extends TypeID<infer Type> ? Type : never;

type Provides<P extends TypeID> = { readonly [T in KeyOf<P>]: TypeOf<P> };

// ---cut---

interface Foo {
    foo(): void;
}

const Foo = typeID("Foo") satisfies TypeID<Foo>;
//    ^? const Foo: TypeID<Foo, "Foo">

const Bar: Provides<typeof Foo> = {
    [Foo]: {
        foo() {}
    }
};
🙁 Actual behavior

Fails to compile with error:

Property 'Foo' is missing in type '{ [x: string]: { foo(): void; }; }' but required in type 'Provides<TypeID<Foo, "Foo">>'.
🙂 Expected behavior

I'd expect it to compile. Rather than widening the literal type to string, I'd expect it to discard the { [typeKey]?: Type } part, since that's pretty much bogus at runtime anyway.

However, it should also not lose the type information for the generic types. For instance, if I change the declaration to...

type TypeID<Type = unknown, ID extends string = string> = ID;

...then it does compile, but so does this...

const Bar: Provides<typeof Foo> = {
    [Foo]: {
        // No error about missing member `foo` here!
    }
};
Additional information about the issue

Possibly related to https://github.com/microsoft/TypeScript/issues/43852#issuecomment-1671115898

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

TypeScript Playground での再現から始め、branded literal の動作を issue #43852 の関連する議論と比較してください。TypeIDKeyOfTypeOfProvides の定義に対する型チェックの経路を追跡してください。完了条件は、例がコンパイルされ、適切な場合には引き続き foo メンバーがないことが報告されることです。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
typescript
領域
compilers, devtools
issue の種類
バグ
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。