3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeScript の条件型 extends ってなに?

Last updated at Posted at 2023-12-12

1. 条件型とは

以下、色の付いているサンプルコードは TypeScript: TS Playground で、コピペで動作します。変数 Result の上をマウスでホバーすると型 true が表示されてわかりやすいです。

//
// T が U の部分型であった場合 true を返す条件型 Conditional Type
//
type Extends<T, U> = T extends U ? true : false

//
// リテラル型はプリミティブ型の部分型
//
type Result0 = Extends<114514, number>  // true
type Result1 = Extends<45450721, number>  // true
type Result2 = Extends<'Kemono Friends', string>  // true
type Result3 = Extends<'serial experiments lain', string>  // true
type Result4 = Extends<true, boolean>  // true
type Result5 = Extends<false, boolean>  // true

2. union distirbutioin

Extends<0, 0 | 1>true になります。しかし Extends<0 | 1, 0>boolean になります。true でもなければ false でもありません、なぜでしょうか?

type Extends<T, U> = T extends U ? true : false
type Result0 = Extends<0, 0 | 1>  // true
type Result1 = Extends<0 | 1, 0>  // boolean

T extends UT が Union 型だった場合、Union 型の要素1つ1つに分解して extends を計算してしまうからです。

type Extends<T, U> = T extends U ? true : false
type Result1 = Extends<0 | 1, 0>  // boolean
// type Result1 = ( Extends<0 | 1, 0>                                              )
// type Result1 = ( 0 | 1 extends 0 ? true : false                                 )
// type Result1 = ((0     extends 0 ? true : false) | ( 1 extends 0 ? true : false))
// type Result1 = ((          true                  |             false           ))
// type Result1 = ( true | false                                                   )
// type Result1 = boolean

対策として以下のように配列 [] で括ると Union Distribution が起こりません。

type Extends<T, U> = T[] extends U[] ? true : false
type Result0 = Extends<0, 0 | 1>  // true
type Result1 = Extends<0 | 1, 0>  // false

また Union Distribution が起こるのはジェネリクスを使用したときのみで、ベタ書きすれば Union Distribution は起こりません。

type Result0 = 0 extends 0 | 1 ? true : false  // true
type Result1 = 0 | 1 extends 0 ? true : false  // false

◯ 御礼

uhyo さんの記事からこの動作をしりました。本当に本当にありがとうございます。リンクを貼ると先方の記事にリンクができてしまうのでタイトル名のみ掲載いたします。

  • TypeScriptの型初級 - Qiita
  • TypeScriptの型演習 - Qiita
  • TypeScriptの型演習(解答・解説編) - Qiita

書籍も買わせていただきました。

◯ 追記

union distribution を避ける際には T[] より [T] が公式の見解らしいです。

3. extends

◯ 機能

TypeScript では extends と書かれていた場合、5 つの機能があります。

  1. class のための extends
  2. interface のための extends
  3. Generics を使った型の制約のための extends
  4. Conditional Types を使った条件分岐のための extends
  5. 4 における infer を使った推論のための extends

2~5 の機能についてリンク先で解説されています。

◯ 意味

上記のように extends には 5 つの機能がありますが、

T extends U

と書かれていた場合、 常に T 型のオブジェクトは、 U 型のオブジェクトが書かれているところでも使えることを表しています。

TypeScript ではここを含めていくつかの場面で S extends T という構文が登場しますが、これは常に「S は T の 部分型 である」という意味です。(P. 101 脚注)
プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで

部分型っていつ使うんですか?どんなときに便利なんですか?って聞かれたら extends のときに使います、extends を使うときに便利ですと答えるのがよいのかなと思います。

4. 公式ドキュメント

いやいや、お前の「T 型のオブジェクトは、 U 型のオブジェクトが書かれているところでも使える」って表現はあってんのかよ?という感じがするのですが。以下は公式ドキュメントの条件型 Conditional Types の項目からの抜粋を見ると、正しくはないけど、妥当かなという気がします。

SomeType extends OtherType ? TrueType : FalseType;

extendsの左側の型が右側の型に代入可能な場合、 最初のブランチ(「true」ブランチ)の型が得られ、そうでない場合、後のブランチ(「false」ブランチ)の型が得られる。
When the type on the left of the extends is assignable to the one on the right, then you’ll get the type in the first branch (the “true” branch); otherwise you’ll get the type in the latter branch (the “false” branch).
TypeScript: Documentation - Conditional Types

条件型, Conditional Type は募集要項の1つに挙げられています。このことから条件型は TypeScript の学習のマイルストーンのひとつになるかなと思いました。

  • 必須スキル
    • 型関数や、conditional typeなどを利用したTypeScriptでの開発経験

ちょっと株式会社 > 求人一覧フロントエンド(Jamstack)エンジニア

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?