質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.40%
TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

解決済

1回答

263閲覧

【TypeScript】boolean extends true ? true : false の結果がジェネリクスにすると変わる理由について

lyel5493

総合スコア1

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

1クリップ

投稿2024/07/16 12:23

以下のコードについてお聞きします。

TypeScript

1type isExtended<T, U> = T extends U ? true : false; 2type X = isExtended<boolean, true>; // X is boolean 3type Y = boolean extends true ? true : false; // Y is false

見ての通り、isExtended は T が U に代入可能であれば true、そうでなければ false を返す型関数です。
X は isExtended<boolean, true> の結果です。
Y は isExtended<boolean, true> を展開して書き下したものです。

X と Y は同じ型になると期待していましたが、実際は X は boolean型、Y は false型 となりました。

なぜこのような動作になるのでしょうか。
distributive conditional type が関係してそうですが正直良くわかりませんでした。

実行環境:
・TypeScript 5.2(ただし、Typescriptのバージョンに依存する挙動とは思えません)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

正解かは分かりませんが見解を記します。(2週間ほど経っているので、解決済みでしたらすみません)

仰るような、Distributive conditional types に関しての説明がドキュメントに記載してあり、こちらがほぼ答えになるかと思われます。

Distributive conditional types are automatically distributed over union types during instantiation. For example, an instantiation of T extends U ? X : Y with the type argument A | B | C for T is resolved as (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y).
(Distributive conditional types は、自動的にユニオン型として分配される。たとえば、T extends U ? X : YT部分がA | B | Cであった場合、(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)と解釈される。)

すなわち、ユニオンを型引数に渡すと、そのユニオンが1つずつ渡されたかのごとく全パターンが網羅される、といったところでしょうか。
これによれば、ご提示いただいた型はこのように言えるかと思います。

typescript

1type isExtended<T, U> = T extends U ? true : false; 2 3type X = isExtended<boolean, true>; 4// これは下記と実質的に同じと見做せる 5type X_2 = (true extends true ? true : false) | (false extends true ? true : false);

booleanも、true | falseの2値しか無いことを踏まえると、実質的にユニオンと見ることができそうです。

この仕組みが、組み込みのExtractなどでも使用されています。
御存知の通り、"Exclude<T, U>"はTからU部分を除いたユニオンを返すというジェネリクスですが、実装はこの様になっています:

typescript

1/** 2 * Exclude from T those types that are assignable to U 3 */ 4type Exclude<T, U> = T extends U ? never : T;

Exclude<boolean, true>はもちろんfalse型となりますが、その実はこの様になっているのでしょう:

typescript

1type Z = Exclude<boolean, true>; 2// これは下記と実質的に同じと見做せる 3type Z_2 = (true extends true ? never : true) | (false extends true ? never : false);

このように考えると、少し納得できたような気がしますが如何でしょうか。

投稿2024/08/01 08:37

編集2024/08/01 08:38
Nao.K

総合スコア40

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

lyel5493

2024/08/04 13:14

丁寧な回答ありがとうございます。 大変参考になりました。 なるほど。やはり、ジェネリクスを用いた場合に型引数の解釈が変わることが根本的な原因みたいですね。 直感的には同じ型になってもらいたいものですが、TypeScriptはむずかしいですね....
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.40%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問