「TypeScriptの型入門」で手を動かす- まとめ

日々少しずつ読みつつ、プレイグラウンドで手を動かしていた、「TypeScriptの型入門」のメモのまとめです。

TypeScriptの型入門 - Qiita

気がつけば、「その14」までブログ投稿してたみたいで。 このまとめでは、その中でも覚えておきたい内容を記載したいと思います。

まとめ

「オブジェクト型再訪」以降がちょっと覚えづらいなぁ。

ここからかな。 tropical-eng83.hatenablog.com

リンク

うーん、リンクつけたはいいが、何が書いてあるかわからんと意味ないかも。 あとで、どの章のメモなのか追記するかな。

「TypeScriptの型入門」で手を動かす - その14

本日は「TypeScriptの型入門」の「Conditional Types」から手を動かします。

qiita.com

プレイグラウンドで手を動かす

Conditional Types

  • TypeScript2.8で久々に登場したやばい型らしい
    • どういう表現やねん
  • 型レベルの条件分岐が可能な型

おすすめ記事

qiita.com

Conditional Typesとは

  • T extends U ? X : Y という構文で表現される
    • 4つの型を使う
  • TがUの部分型ならX、そうでなければYになる
  • 表現力がすざまじく、いろんな問題を解決できる

mapped typesの限界

  • deepなマッピングできないという問題がある
    • Readonlyでは、プロパティは全てreadonlyにできるが、プロパティがオブジェクトだった場合に、そのオブジェクトのプロパティはreadonlyにならない
  • deepなreadonlyの方が需要があった
  • 再帰的にDeepReadonlyを適用するtypeのサンプルコードを実行してみたが、特にエラーにならなかたった。。。
    • TypeScriptが頭良くなったのかな。

conditionaly typeによる、DeepReadonly

まぁ、内容は70%くらいしか理解できなかったが、プレイグラウンドの方に色々 検証コードは書けたので、理解は深まった。とする。

conditional typeにおける型マッチング

  • conditional typenのさらなる強力な機能
    • conditional typeの条件部で新たな型変数を導入できる
      • infer R
  • 例: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」から手を動かします

qiita.com

プレイグラウンドで手を動かす

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型

qiita.com

おわりに

型の表現力がけっこうましたんだろうなというのはわかった。

Mapped Typeがないと、anyでやらざる得ない、便利関数の利用元で適切に型変換しないといけない、とかあったのかもしれないと思った。

「TypeScriptの型入門」で手を動かす - その12

本日は「TypeScriptの型入門」の「typeof」から手を動かします!

qiita.com

プレイグラウンドで手を動かす

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型と{}型」から手を動かします!

qiita.com

プレイグラウンドで手を動かす

object型と{}型

  • object型とは「プリミティブ以外の値の型」
  • オブジェクトのみを引数に受け取る関数を表現するための型
    • Object.create()
  • {}という型
    • 何もプロパティが無いオブジェクト型
    • 構造的部分型により、{ foo: string}のような型も{}として扱える
    • 任意のオブジェクトを表す型としては使えない
      • const o:{} = 3 とか受け付けてしまう
    • undefinedとnullは受け付けない
    • {}はundefinedとnull以外はなんでも受け入れるとても弱い型

weak type

  • オプショナルなプロパティしかもたない型のこと
  • 特殊な処理が行われる
    • 以下は本来はweak typeの型として妥当なはず
      • オプショナルなプロパティをどれも持たない
      • 型で定義されていない余計なプロパティがある
        • 構造的部分型なので。余計なプロパティはあってもよい。
    • weak typeでは特殊な制限がかかる
      • weak typeが持つプロパティを1つ以上持って無いとエラー
  • オプショナルプロパティはオプションを表現するオブジェクトで利用されるので、この制限はむしろかかってほしい

unknown

  • 本当に最も弱い型
    • {}の後に追加されたのかな
  • top型、never型のちょうど逆、全ての型を部分型として持つ、部分型関係の頂点
  • 任意の型を受け付けるという意味でanyと同じ
    • できることが制限される分、anyよりは良い選択肢
  • unknown型は、できることが制限される
    • 数値の足し算もできない
    • typeofで型の絞り込みが必須なので、その点anyよりは良い

unknown型とvoid型の意外な関係

  • unknown型とvoid型は似ているところがある
  • 関数の返り値の型としてvoid型が登場する場合
    • void型は返り値が無い関数の型で、undefinedは入ることができたかな
      • void型で定義された変数に直接"123"とか値入れるとエラー
  • なんでこんなことができるのかはピンときていない
    • 関数を実行したいが、返り値に興味ない場合というのを表現できるらしい
    • ()=>func1() のようなアロー関数経由でfunc1を実行したい
      • 本来なら()=>numberかもだが、返り値に興味ないので()=>voidとして扱いたい
    • 確かに、あるような気もする。ライブラリの関数実行して返り値は使わない場合とか
  • というと、void型はundefinedが入っているとは限らず、何が入っているかわからない
    • つまり、unknown型と同じ、制限もかかっている

void型は何が入っているかわからない型、void型を返す関数の型といっても、値を返す関数を代入可能、ちょっと特殊?な動きだなぁ。

これで実現ができると言っていることは、たしかにそうだと思うのだけど。

今日は一旦ここまで、次は「typeof」から手を動かします!

「TypeScriptの型入門」で手を動かす - その10

本日は「TypeScriptの型入門」の「テンプレートリテラル型」から手を動かします!

qiita.com

テンプレートリテラル

  • 特定の形の文字列のみを受け入れる型
  • リテラル型より少し柔軟
    • リテラル型はただ一種類の文字列のみを受け入れる
  • Hello, ${string}というように書ける
    • Helloで始まる文字列を受け入れる型
  • TypeScript4.1で導入されたときに話題になったらしい
    • conditional types (infer)と組み合わせると型レベル計算における文字列操作が可能になったから
      • あまりピンときてない

as const

  • readonlyに関連する話題
  • as const型推論の方法を指示する構文
    • 各種リテラルに付加することができて、その値が書き換えを意図していない値であることを表す
  • var foo = '123' as constとすると、string型じゃなくて"123"型となる
    • これだけならconstで定義すれば良いだけ
  • オブジェクトリテラル
    • プロパティをreadonlyにできる
    • オブジェクトをconstで定義しても、プロパティは通常プロパティは変更できる
      • as constつけると変更できなくなる
    • プロパティの型はリテラル型になる
  • テンプレート文字列リテラルに対するas const
    • テンプレート文字列リテラルは通常string型
    • as constをつけると、テンプレートリテラル型になる
      • Hello, ${string}型とか。
  • as constはリテラル型推論で型を広げて欲しくないときに使うことができる

as const で推論される型まとめ

プレイグラウンドで手を動かす

本日はここまで次は「object型と{}型」から手を動かします!

「TypeScriptの型入門」で手を動かす - その9

本日は「TypeScriptの型入門」の「readnlyな配列とタプル」から手を動かします!

qiita.com

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は配列型として推論される
  • タプルの型を取りたいのに、配列型になっちゃう場合に使えるとのこと

プレイグラウンドで手を動かす

本日はここまで、次は「テンプレートリテラル型」から手を動かします!