ペロハム大学

ヤルゾー

プロを目指す人のためのTypeScript入門 読書メモ⑩ 第6章(3)

■ 思ったこと

今回は区切りがなかなか付けられない、一個一個が難しいなど理由から更新に時間がかかった!そしてその間にVA-11 Hall-A一週目もクリアした!家賃払えなかった!もっかいやる!

■ 第6章 高度な型(6.3~6.8.6)

ユニオン型の素晴らしいところ

  • 型の絞り込みに対応しているところだ!
  • コントロールフロー解析ともいう
  • 等価演算子(===)とか使って早期にこれはこの型、と絞っていく

typeof演算子

  • typeof 式という形の、式!
  • 式の型によって対応する文字列を返す、typeof 1なら"number"というように
  • string | numberのようなユニオン型に有効
  • if (typeof value === "number")みたいに受け取った値で処理を絞れる、elseは絶対stringだしね

代数型データ型(タグ付きユニオン、直和型)

  • という機能はTypeScriptにはないが、オブジェクト型とユニオン型を用いて代用できる
  • プロパティにユニオン型に対応するリテラル型をあらかじめ持たせといて、判別に使うのだ
    • 判別用の情報はタグと呼ばれる
  • データを使う側は、タグを頼りに型を絞ればOK
  • おかげでユニオン型がなんの型の時は、がやりやすくなる!

switch文でも絞り込める

  • ユニオン型の構成要素が膨大になるならこっちも検討するべし、網羅されてない型があると戻り値エラーなったりするので良いかんじ

lookup型

  • 構文はオブジェクト型[文字列のリテラル型]
  • 例の通り、型情報を再利用できるのさ
export type Human = {
  type: "human";
  name: string;
  age: number;
};

/**
 * ageをセットする関数
 *  @param human Human
 *  @param age  loo
kup型(number) 実際はHuman型のageプロパティの型
 *        …元の型が変わってもこれならOK。
 */
const setAge = (human: Human, age: Human["age"]) => {
// 処理
}

keyof型

  • 構文はkeyof 型
  • すでにある型の、プロパティを全部文字列リテラルの型にしちゃう
  • なんちゃら型のプロパティを
export type Human = {
  name: string;
  age: number;
};

// keyof HumanでHumanKeys型ができた!
// Humanオブジェクトはnameとageなので、keyof Humanは"name" | "age"型の意味
// プロパティは文字列のリテラル型となる
type HumanKeys = keyof Human;

// HumanKeys 型は文字列"name"か"age"だけが代入可能
let key: HumanKeys = "name";
key = "age";

keyof型・lookup型はジェネリクスと一緒に使ってみよう

  • lookup型はオブジェクトのプロパティ名いうとその型になるやつ
  • keyof型はオブジェクト指定するとそのプロパティ全部入りの文字列リテラル型のなる
  • ジェネリクスは型が決まってない時に使う、型引数を持つ関数だったね
  • 全部まぜると、こんな感じ
// オブジェクトのプロパティを名を受け取って中身を返す関数
// extendsを型引数の後ろにつけると、Kを必ずTの部分型にする制約がつく
// 意味としては変なのkeyに持ってこないように。
const get = <T, K extends keyof T>(obj: T, key: K): T[K] => {
  // オブジェクトの中のプロパティ値を取得する
  return obj[key];
};

type Cocktail = {
  adelhyde?: number;
  bronsonExtract?: number;
  powderedDelta?: number;
  flanergide?: number;
  karmotrine?: number;
  type: string[];
};

let karmotrine;
const setkarmotrine = (quantity: number) => {
  karmotrine = quantity;
};

setkarmotrine(4);
const sugarRush: Cocktail = {
  adelhyde: 2,
  powderedDelta: 1,
  karmotrine: karmotrine,
  type: ["甘め", "ガーリー", "ハッピー"],
};

const cocktailType = get(sugarRush, "type");
// const get: <Cocktail, "type">(obj: Cocktail, key: "type") => string[]
console.log(`シュガーラッシュは${cocktailType}なカクテルです。`);
// [console]シュガーラッシュは甘め,ガーリー,ハッピーなカクテルです。

const karmotrineQuantity = get(sugarRush, "karmotrine");
// const get: <Cocktail, "karmotrine">(obj: Cocktail, key: "karmotrine") => number | undefined
console.log(`カルモトリンは${karmotrineQuantity}%含まれています。`);
// [console]カルモトリンは4%含まれています。

keyof型のkeyはnumber型になる時もある

  • keyof型のkeyですが、実のところ文字列リテラル型だけではなく、number型でもOKなのです
  • 型のプロパティが数値の時のお話

アサーション

  • って何!?と思うが、できるだけ使用を避けた方がいいらしい、そうなの?
    • 型安全を意図的に破壊しちゃうんだって…
    • じゃあいつ使うのか??それは、型推論が完璧じゃない時にTypeScriptに正しい挙動を人間が教えてあげたい時なのよ
  • 構文は式 as 型、式の型を強制的に変えたるぜ!
  • でも、実際に値がその型に変わるわけではない…コンパイル時に型がコレだと認識してくれるだけ

式 as const

  • これはよく見る!
  • 式部分の型推論に対して、4つのすてきな効果がある
  • つまり…全部後から変更不可になるってこと!!
  • おさらいだけど、タプル型は要素数が固定されていて、いろんな型を入れられる型ね
const name1 = ["a", "b", "c", "d", "e"]
// const name1: string[]
const name2 = ["neko", "inu", "car"] as const;
// const name2: readonly ["neko", "inu", "car"]

// 配列はnumber型のインデックスに要素を代入しているオブジェクトなので
// インデックスの代わりにnumberを使うと全部とれる
type Names = (typeof name2)[number];
// type Names = "neko" | "inu" | "car"

恐怖!any型

  • 型安全を破壊するやつと言ったら、これ以上の存在はない
    • なんといっても、型チェックを無効化してくるのだ
    • つまり、何を代入してもエラーにならない!
  • とりあえず、使うな!どうしても必要ならasとか使え!

実は安全!unknown型

  • こいつもanyよろしくなんでも入る型だが、可能な操作が限られているので安全
  • 何が入ってるかわからないからunknown型
  • なんでも受け取って、typeofで絞ってあげるのがいい感じ

一応知っとけ!もっと高度な型

  • object型
    • そうそう、オブジェクトはプリミティブ以外のすべてだったね
    • ゆーて、空のオブジェクト型({}型)にはリテラル型でもなんでも入ってしまうので…本当にオブジェクトのみに制限したいなら不十分
    • というときに使う!!
  • never型
    • 何にも入らない型、unknown型の反対!
    • しかし、仮に受け取ったneverはどんな型にも代入し放題!な、なんそれ!
    • でも!型安全は守られるよ、だってneverに当てはまる型なんてないんですもの!
    • それってつまり、never型はすべての型の部分型・・ともいえる
  • 型述語(ユーザー定義ガード)
    • 型の絞り込みを行う仕組み!が、型安全の破壊者でもあるので注意(まだマシではある)
    • 特徴としては、返り値の型が引数名 is 型といった型述語になるよ
    • const 関数= (value: unknown): value is string | number的な
    • 実際の返り値はboolean、trueなら引数名に与えられた値を型として認めてくれるらしい。つまり使用する型が絞り込める
    • こんなんしなくてもifとかで絞ればいいじゃんと思うが、得てして構文エラーになるのでこれが必要

可変長タプル型

  • タプル型の中に...配列型を含めたもの
  • 注意点としては、一個のタプル型の中で一回しか使えないこと、オプショナルな要素(?)を...配列型より後ろで使えないこと
  • スプレッド構文のように型を展開効果もあり
// ...配列型は、その部分にその配列型の要素が任意個入ることを示す
type NumberAndStrings = [number,...string[]]

const arr1: NumberAndStrings = [25,"blow","rin","maliya"];
// 0個もOK
const arr2: NumberAndStrings = [25];

type Animals = ["neko","inu","usagi"];
type AnimalMember = ["alpaca",...Animals]
// type AnimalMember = ["alpaca", "neko", "inu", "usagi"]

組み込みの型

  • mapped types,conditional typesは詰みそうだったので飛ばしてしまいました・・
  • しかし!上記の中でも有用な機能が組込み型として標準ライブラリに用意されているとな!以下参照!
// T1にのプロパティをすべてReadonlyに
type T1 = Readonly<{
  name: string;
  age: number;
}>;

// T2のプロパティをすべてオプショナルに
type T2 = Partial<{
  name: string;
  age: number;
}>;

// T3のプロパティから一部をピックアップして新しいオブジェクト作る
type T3 = Pick<
  {
    name: string;
    age: number;
  },
  "age"
>;

// T4のプロパティから一部を取り除いた新しいオブジェクト作る
type T4 = Omit<
  {
    name: string;
    age: number;
  },
  "age"
>;

type Union = "te" | "st" | 1 | 2 | undefined;
// Unionからstring型のみ抜き出した新しいユニオン型作る
type T5 = Extract<Union, string>;

// Unionからstring型を取り除いた新しいユニオン型作る
type T6 = Exclude<Union, string>;

// Unionからundefined、nullを取り除いた新しいユニオン型作る
type T7 = NonNullable<Union>;

■ 感想

長くなり過ぎた!!力試しはなんとかできたけど、型引数を積極的に使えてなかったりswitchに苦手意識があったりで回答と完璧に一致はできず!使ってかないと覚えないね!そんで、これを書いている間に色々あったが。。55時間特番、楽しんでますタイムフリーで!0大集合の野田とフワちゃんの絡みがなんか好き(笑) そして世界樹リマスター!!たのしみすぎる!