「TypeScriptの型入門」で手を動かす- まとめ
日々少しずつ読みつつ、プレイグラウンドで手を動かしていた、「TypeScriptの型入門」のメモのまとめです。
気がつけば、「その14」までブログ投稿してたみたいで。 このまとめでは、その中でも覚えておきたい内容を記載したいと思います。
まとめ
「オブジェクト型再訪」以降がちょっと覚えづらいなぁ。
ここからかな。 tropical-eng83.hatenablog.com
リンク
うーん、リンクつけたはいいが、何が書いてあるかわからんと意味ないかも。 あとで、どの章のメモなのか追記するかな。
- 「TypeScriptの型入門」で手を動かす - その1 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その2 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その3 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その4 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その5 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その6 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その7 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その8 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その9 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その9 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その10 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その11 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その12 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その13 - tropical_eng83’s blog
- 「TypeScriptの型入門」で手を動かす - その14 - tropical_eng83’s blog
「TypeScriptの型入門」で手を動かす - その14
本日は「TypeScriptの型入門」の「Conditional Types」から手を動かします。
Conditional Types
- TypeScript2.8で久々に登場したやばい型らしい
- どういう表現やねん
- 型レベルの条件分岐が可能な型
おすすめ記事
Conditional Typesとは
T extends U ? X : Y
という構文で表現される- 4つの型を使う
- TがUの部分型ならX、そうでなければYになる
- 表現力がすざまじく、いろんな問題を解決できる
mapped typesの限界
- deepなマッピングできないという問題がある
- Readonly
では、プロパティは全てreadonlyにできるが、プロパティがオブジェクトだった場合に、そのオブジェクトのプロパティはreadonlyにならない
- Readonly
- deepなreadonlyの方が需要があった
- へー、そうなんだ。どういうユースケースだろうか
- 再帰的にDeepReadonly
を適用するtypeのサンプルコードを実行してみたが、特にエラーにならなかたった。。。 - TypeScriptが頭良くなったのかな。
conditionaly typeによる、DeepReadonly
- くそ難しい
- DeepReadonly
の本質は、conditional typeが遅延評価されるところにある - らしい
- conditional typeで評価時に無限に再帰することを防いでいる
- ???て感じ
まぁ、内容は70%くらいしか理解できなかったが、プレイグラウンドの方に色々 検証コードは書けたので、理解は深まった。とする。
conditional typeにおける型マッチング
- conditional typenのさらなる強力な機能
- conditional typeの条件部で新たな型変数を導入できる
infer R
- conditional typeの条件部で新たな型変数を導入できる
- 例:
type ReturnType<T> = T extends ( ...args: any[]) => Infer R ? R : T
- T が関数の型のときは返り値は関数の返り値のR型
- Tが関数の型でないときは返り値は、T型
infer R
がポイント。conditional typeの条件分岐で型変数を定義できる(...args: any[]) => 何らかの型
が型変数を使えない場合T extends [ 条件部 ] ? [trueの場合] : [falseの場合]
- 条件部で
infer R
で型変数を定義して、分岐部分(trueの場合)で利用できる - falseの場合、ではRは使えない。そもそも条件部に合致してないのでRが存在しないかもだから
- 条件部で
infer
を複数箇所に現れることも可能- 記事の例では、以下が興味深かった
- Uがconvariantな位置にある場合はユニオン型で表現される
- Vがcontravariantな位置にあるアバイはintersection型で表現される
- メソッド型の引数でinferを使う
conditional typesによる文字列操作
infer
とテンプレートリテラル型を組み合わせることで、型レベルの文字列操作が可能に"Hello, world!"'型というリテラル型から
world`部分を抜き出すことができる
おわりに
終わったー、これも時間かかった。時間かかりすぎて忘れてるのもあるなw
そして、これで入門編という。
続編がでてるらしいので、それをやってみるかー
「TypeScriptの型入門」で手を動かす - その13
本日は「TypeScriptの型入門」の「Mapped Types」から手を動かします
Mapped Types
- keyof , Lookup Types T[K], Mapped Typesdが同じタイミングで実装された
- ` { [ P in K] : T } という構文を持つ型
- P は型変数。KとTは何らかなの型
- Kはstringの部分型である必要がある
- 「K型の値として可能な各文字列Pに対して、型TをもつプロパティPが存在するようなオブジェクトの型
- 例:
{ [ P in 'foo' | 'bar'] : number }
{ foo: number, bar: number}
と同じ意味
使い所
- 各プロパティの型を一括でnull許容するとかreadonlyつけるとか
type PropNullable<T> = {[P in keyof T]: T[P] | null};
- Partial
, Required とか。
実践的な例
- Diff型
おわりに
型の表現力がけっこうましたんだろうなというのはわかった。
Mapped Typeがないと、anyでやらざる得ない、便利関数の利用元で適切に型変換しないといけない、とかあったのかもしれないと思った。
「TypeScriptの型入門」で手を動かす - その12
本日は「TypeScriptの型入門」の「typeof」から手を動かします!
typeof
typeof 変数
と書くと、その変数の型が得られる
keyof
- いよいよTypeScriptのよくわからないところらしい。。
- ある
T
を型とすると、keyof T
という型の構文がある。keyof T
は「T
のプロパティ名全ての型」
kyeof T
は型として使えるのか- T型のプロパティ名を列挙したunion型かな。
interface MyObj { foo: string; bar: number; } let key: keyof MyObj; // 'foo' | 'bar'
プロパティがsymbol
- プロパティ名はシンボルの可能性もある
- keyof型はシンボルの型を含む可能性がある
keyof ( typeof obj)
でシンボル型を含むユニオン型を得られる’foo’ | typeof symb
となる。symbol型ではない。シンボルは一つずつ異なるため
プロパティがnumberの部分型
- keyof型には
number
の部分型含まれる場合もある。- 数値リテラルを使ってプロパティを宣言した場合
- JSではプロパティ名に数値は使えない。勝手に文字列に変換される。
- TypeScriptでは数値をプロパティ名に使用した場合は型の上ではそれを保つ
プロパティがインデックスシグネチャのとき
[foo: string]: number
のとき、keyof はstring | number
になる- numberでもどうせstringに変換されるから
[foo: number]: number
のときは、keyof はnumber
になる
Lookup Types T[K]
- keyofとセットで使われることが多い
- Kがプロパティ名の型であるとき、T[K]はT型のそのプロパティの型
- keyofがプロパティ名の型を取得する構文だから一緒に使われるんだろうな
- MyObj型にfooプロパティがあり、fooプロパティはstring型の場合
- MyObj['foo']型はstring型
- Kはkeyof Tの部分型である必要がある。Tのプロパティ名の型なので
今日はここまで、次は「Mapped Types」から手を動かします!
残りわずかかな。もうひと踏ん張り頑張ろう!
「TypeScriptの型入門」で手を動かす - その11
今日は「「TypeScriptの型入門」の「object型と{}型」から手を動かします!
object型と{}型
- object型とは「プリミティブ以外の値の型」
- オブジェクトのみを引数に受け取る関数を表現するための型
- Object.create()
{}
という型- 何もプロパティが無いオブジェクト型
- 構造的部分型により、
{ foo: string}
のような型も{}
として扱える - 任意のオブジェクトを表す型としては使えない
const o:{} = 3
とか受け付けてしまう
- undefinedとnullは受け付けない
{}
はundefinedとnull以外はなんでも受け入れるとても弱い型
weak type
- オプショナルなプロパティしかもたない型のこと
- 特殊な処理が行われる
- 以下は本来はweak typeの型として妥当なはず
- オプショナルなプロパティをどれも持たない
- 型で定義されていない余計なプロパティがある
- 構造的部分型なので。余計なプロパティはあってもよい。
- weak typeでは特殊な制限がかかる
- weak typeが持つプロパティを1つ以上持って無いとエラー
- 以下は本来はweak typeの型として妥当なはず
- オプショナルプロパティはオプションを表現するオブジェクトで利用されるので、この制限はむしろかかってほしい
unknown
- 本当に最も弱い型
{}
の後に追加されたのかな
- top型、never型のちょうど逆、全ての型を部分型として持つ、部分型関係の頂点
- 任意の型を受け付けるという意味でanyと同じ
- できることが制限される分、anyよりは良い選択肢
- unknown型は、できることが制限される
- 数値の足し算もできない
- typeofで型の絞り込みが必須なので、その点anyよりは良い
unknown型とvoid型の意外な関係
- unknown型とvoid型は似ているところがある
- 関数の返り値の型としてvoid型が登場する場合
- void型は返り値が無い関数の型で、undefinedは入ることができたかな
- void型で定義された変数に直接"123"とか値入れるとエラー
- void型は返り値が無い関数の型で、undefinedは入ることができたかな
- なんでこんなことができるのかはピンときていない
- 関数を実行したいが、返り値に興味ない場合というのを表現できるらしい
()=>func1()
のようなアロー関数経由でfunc1を実行したい- 本来なら
()=>number
かもだが、返り値に興味ないので()=>void
として扱いたい
- 本来なら
- 確かに、あるような気もする。ライブラリの関数実行して返り値は使わない場合とか
- というと、void型はundefinedが入っているとは限らず、何が入っているかわからない
- つまり、unknown型と同じ、制限もかかっている
void型は何が入っているかわからない型、void型を返す関数の型といっても、値を返す関数を代入可能、ちょっと特殊?な動きだなぁ。
これで実現ができると言っていることは、たしかにそうだと思うのだけど。
今日は一旦ここまで、次は「typeof」から手を動かします!
「TypeScriptの型入門」で手を動かす - その10
本日は「TypeScriptの型入門」の「テンプレートリテラル型」から手を動かします!
テンプレートリテラル型
- 特定の形の文字列のみを受け入れる型
- リテラル型より少し柔軟
- リテラル型はただ一種類の文字列のみを受け入れる
Hello, ${string}
というように書ける- Helloで始まる文字列を受け入れる型
- TypeScript4.1で導入されたときに話題になったらしい
- conditional types (infer)と組み合わせると型レベル計算における文字列操作が可能になったから
- あまりピンときてない
- conditional types (infer)と組み合わせると型レベル計算における文字列操作が可能になったから
as const
readonly
に関連する話題as const
は型推論の方法を指示する構文- 各種リテラルに付加することができて、その値が書き換えを意図していない値であることを表す
var foo = '123' as const
とすると、string型じゃなくて"123"型となる- これだけなら
const
で定義すれば良いだけ
- これだけなら
- オブジェクトリテラル
- プロパティをreadonlyにできる
- オブジェクトをconstで定義しても、プロパティは通常プロパティは変更できる
as const
つけると変更できなくなる
- プロパティの型はリテラル型になる
- テンプレート文字列リテラルに対するas const
- as constはリテラルの型推論で型を広げて欲しくないときに使うことができる
as const で推論される型まとめ
- 文字列、数値、真偽値はリテラル型になる
- テンプレート文字列リテラルはテンプレートリテラル型になる
- オブジェクトリテラルはプロパティがreadonlyになる
- 配列リテラルはreadonlyタプル型になる
本日はここまで次は「object型と{}型」から手を動かします!
「TypeScriptの型入門」で手を動かす - その9
本日は「TypeScriptの型入門」の「readnlyな配列とタプル」から手を動かします!
readonlyな配列とタプル
- 配列型やタプル型においてもreadonlyの概念が存在する
- オブジェクト型のプロパティは前やった
- 配列やタプル全体がreadonlyかどうかを指定できる
- プロパティごとの制御はできない
配列
readonly T[]
とかく- readonlyの配列ではpushとか破壊的メソッドは除去されている
- ReadonlyArray
ともかける
タプル
readonly [ string ,number]
のように書く- 配列と同様にpushなどは除去されてる
- readonlyではない普通のタプルでも除去されて良い気もするが
Variadic Tuple Types
- タプルの定義にスプレッド構文を使って、新しいタプルを作ることができる
[ string, ...タプル型, number]
みたいな
- この機能のすごいところは
...型変数
の形で使うことができて型推論の材料にできることらしい- [T, ...Rest]という型を定義すると[1,2,3,"foo"]がT=1, Rest=[2,3,"foo"]となる
- 例だと
Rest
は型じゃなくて、型変数(Tとかと一緒)で、それでも...
が使えるという点が便利ということかな - removeFirstの例だと、先頭の1要素を除いた残りのタプルの型を推論できる
- 型引数を
...T
の形で行う場合は、型引数が配列型またはタプル型であるという成約をつけて宣言する必要があるextends readonly unknown[]
とすると良い
[...T]とTの違い
[...T]
とT
はちょっとした違いがある- 引数にタプルを取る関数で引数の型をTまたは[...T]とし、返り値をT型する場合
- Tは何型?
[...T]
はタプル型として推論されるT
は配列型として推論される
- Tは何型?
- タプルの型を取りたいのに、配列型になっちゃう場合に使えるとのこと
本日はここまで、次は「テンプレートリテラル型」から手を動かします!