とある休日
娘「パパ」
ワイ「なんや?娘ちゃん」
娘「あのね、お友達が」
娘「TypeScriptの便利な型を忘れちゃったの」
ワイ「そうなんや」
ワイ「便利な型を忘れてもうて、どうなってんねんそれ」
ワイ「ほな、ワイも一緒に考えてあげるから、その型の特徴を教えてみてよ」
娘「ええとね、例えば」
type User = {
firstName: string
familyName: string
}
娘「↑この型を」
type PartialUser = {
firstName?: string // オプショナルになった
familyName?: string // オプショナルになった
}
娘「↑こんな型に変身させてくれる」
娘「そんな便利な型らしいんだけど」
ワイ「おー、Partial<T>やないか」
ワイ「その特徴はもう完全にコーンフレーク・・・やなくてPartial<T>やがな」
娘「コーンフレーク?」
ワイ「いやPartial<T>のことやがな」
Partial<T>
// 全てのプロパティをオプショナルにした PartialUser 型を定義する
type PartialUser = Partial<User>
ワイ「↑こうするだけやがな」
ワイ「型を渡すと、それを元に別の型を返してくれる」
ワイ「型の関数みたいな、便利なやつやで」
娘「でも分からないの」
ワイ「何が分からへんのよ」
娘「いや私もPartial<T>だと思ったんだけどね」
娘「お友達が言うには」
type User = {
tag: string
firstName: string
familyName: string
}
娘「↑こんな風に、tagっていうstring型のプロパティがあった場合には」
娘「その便利な型を使っても」
type PartialUser = {
tag: string // ここだけオプショナルにならない
firstName?: string
familyName?: string
}
娘「↑こんな風に、tagだけはオプショナルにならないって言ってた」
ワイ「ほなPartial<T>と違うか」
ワイ「Partial<T>は、全てのプロパティをオプショナルにしてしまうもんな」
娘「そうなの」
自作の便利型
ワイ「ほな、こんな便利な型を自作すればええんとちゃうか?」
// tag 以外のプロパティをオプショナルにしてくれる便利型
type PartialWithoutTag<T> = Partial<T> & { tag: string }
ワイ「↑こうや」
ワイ「この型を使えば」
type User = {
tag: string
firstName: string
familyName: string
}
ワイ「↑この型を」
type PartialUser = PartialWithoutTag<User>
ワイ「↑こうして」
type PartialUser = {
tag: string // ここだけオプショナルにならない
firstName?: string
familyName?: string
}
ワイ「↑こんな型を作ってくれるイメージやで」
娘「でも、その型って」
- 全てのプロパティをオプショナルにしつつ
tagというプロパティを追加する
娘「↑こんな感じの型だから」
type Hoge = {
aaa: string
}
娘「↑こう、元々tagを持っていない型を渡したときに」
type Hoge = {
tag: string // プロパティが増えている
aaa?: string
}
娘「↑こう、tagプロパティが増えちゃうでしょ」
ワイ「せやな」
勝手にtagプロパティを増やして欲しくはない
娘「元々tagプロパティがない型を渡した場合には」
娘「tagプロパティを増やして欲しくはないの」
ワイ「ぐぬぬ」
ワイ「つまり・・・」
- 元の型を加工して返してくれる便利な型
- 全てのプロパティをオプショナルにしてくれる
-
tagプロパティがあった場合
→ そこだけはオプショナルにしない -
tagプロパティがない場合
→ ないままでいい
-
ワイ「↑こういうことか・・・」
娘「そう」
娘「そういう、条件によって変わる型を作りたいの」
ワイ「ほなConditional Typesやないか」
Conditional Types
ワイ「ええと」
type PartialWithoutTag<T> =
T extends { tag: string } // 条件
? Partial<T> & { tag: string } // 真の場合に返す型
: Partial<T> // 偽の場合に返す型
ワイ「↑こうやな」
ワイ「三項演算子みたいに書くねん」
ワイ「いや、四項演算子か」
娘「そっか、なるほど」
娘「Conditional Typesを使えばよかったんだね」
娘「ちょっと、コメントを変えてみていい?」
ワイ「おお、ええで!」
娘「ありがとう」
娘「えっと・・・」
type PartialWithoutTag<T> =
T extends { tag: string } // tag という string 型のプロパティを持っていた場合
? Partial<T> & { tag: string } // tag プロパティ以外をオプショナルにする
: Partial<T> // そうでない場合、全てのプロパティをオプショナルにする
娘「↑こういうことだね!」
ワイ「せやな!」
娘「渡された型Tが、tagプロパティを持っていた場合には」
娘「tag以外をオプショナルにしてくれるし」
娘「そうでない場合には」
娘「全てのプロパティをオプショナルにしてくれる・・・」
娘「できたね!」
娘「パパ、ありがとう!」
ワイ「かめへん、かめへん!」
娘「でも分からないの」
ワイ「いやまだあんのかい」
tagプロパティがstring型じゃないこともある
娘「そういえばお友達が」
お友達「
tagプロパティは必ずしもstring型じゃないんだ」
娘「↑こう言ってた」
娘「tagプロパティの型は、場合によって色々だって」
ワイ「ぐぬぬ」
ワイ「ほなany型やないかい」
ワイ「その特徴はもう、なんでも許してくれる便利なany型や!」
娘「any型は危険だから、tsconfig.jsonで禁止してるってお友達が言ってた」
ワイ「ほなany型と違うか」
ワイ「ええと、さっきの型は」
- tag という string 型のプロパティがあった場合
ワイ「↑こういう条件やったけど」
ワイ「それを」
- tag という何らかの型のプロパティがあった場合
ワイ「↑こういう条件に変えなアカン訳やな」
ワイ「どうすればええんや・・・?」
娘「あ、分かった!」
inferを使う
娘「ええと、extendsの右側の部分だけど」
娘「{ tag: string }とは限らないから」
娘「条件を広げるために」
娘「{ tag: infer U }に変えるね」
type PartialWithoutTag<T> =
T extends { tag: infer U } // string から infer U に変更
? Partial<T> & { tag: string }
: Partial<T>
娘「↑こうだね」
娘「extendsの右側で{ tag: infer U }ってすることで」
娘「tagプロパティの型を推論して」
娘「Uという型変数に入れてくれるの」
ワイ「おお」
ワイ「tagプロパティの型、つまり何か分からん型に」
ワイ「Uっていう名前をつけるイメージやな」
娘「うん」
ワイ「なるほどな」
- tag という string 型のプロパティがあった場合
ワイ「↑こういう条件を」
- tag という何らかの型のプロパティがあった場合
ワイ「↑こういう条件に広げたい場合に」
ワイ「inferが使えるんやな」
娘「そうだね」
娘「そして、その型変数Uを」
娘「後ろの部分で使ってあげないといけないから」
type PartialWithoutTag<T> =
T extends { tag: infer U }
? Partial<T> & { tag: U } // string から U に変更
: Partial<T>
娘「↑こうだね!」
ワイ「なるほどな」
ワイ「コメントを付け直すとすると」
type PartialWithoutTag<T> =
T extends { tag: infer U } // tag プロパティがあった場合、その型を U とする
? Partial<T> & { tag: U } // tag プロパティの型は U のまま引き継ぐ
: Partial<T>
ワイ「↑こんな感じやね」
娘「そうだね」
娘「ちなみにinferというのは、推論するって意味だね」
ワイ「なるほどな」
ワイ「tagプロパティの型を推論して、Uという型変数に入れてくれる訳やもんな」
娘「そうだね」
ワイ「そうかー」
ワイ「inferってよく分かってなかったけど」
ワイ「Conditional Typesのextendsの右側で使うものだったんやな」
ワイ「そんで、条件を汎用的にできるんやな」
娘「そんな感じだね」
ワイ「さすが娘ちゃんや」
ワイ「これで、自作の便利型が1つ作れたやないか」
娘「えへへ」
まとめ
-
A extends B ? C : Dという形式で、条件分岐のロジックをもった型を作れる-
A extends Bが条件にあたる -
Cは真の場合に返す型 -
Dは偽の場合に返す型
-
-
Bの部分でinferというキーワードを使うことができる- 型を推論し型変数に格納できる
- 何か分からない型でも条件に使うことができる
- 型変数に格納した型は、返す型を書くときに使える
その日の夜
ワイ「Conditional Typesを忘れてしまって、思い出したいなんて」
ワイ「変わったお友達がおるんやね」
ワイ「一体どんなお友達なん?」
娘「そんなお友達はいないの」
ワイ「ファッ!?」
娘「パパに、Conditional Typesとinferの使い方を理解して欲しくて」
娘「仮の問いをしてみたの」
ワイ「そ、そうなん・・・?」
ワイ「ま、まあ、おかげで勉強になったからええわ」
ワイ「ありがとうやで」
〜おしまい〜