Kodokon kodokon.com

マップ型とキーの再マッピング: 型を変換する

準同型なマップ型がどのように修飾子を保持するか、そして as 節がどのようにキーを名前変更・フィルタリングするかを理解する。

8 分 · 3 問

このレッスンを Kodokon で開く

マップ型は、あるキーの集合を反復処理して新しいオブジェクト型を生成します。[K in keyof T] の形は準同型と呼ばれます: コンパイラは構造が T に由来することを知っており、各元プロパティの readonly および ? 修飾子を自動的にコピーします。+- の接頭辞を使うと、これらの修飾子を明示的に追加・削除できます: これこそが PartialRequiredReadonly の背後にある内部の仕組みそのものです。

TYPESCRIPT
type Mutable<T> = {
  -readonly [K in keyof T]-?: T[K];
};

interface Config {
  readonly host?: string;
  readonly port?: number;
}

type Draft = Mutable<Config>;
// { host: string; port: number }
- 接頭辞が readonly とオプショナル性を取り除く。

TypeScript 4.1 以降、as 節はキーの再マッピングを可能にします: 各キーはその場で名前変更でき、典型的にはテンプレートリテラル型を通じて行います。そして、この節が never を生成すると、そのキーは単に結果から削除されます: これがプロパティフィルタリングの公式な仕組みです。例に出てくる & string に注目してください: keyof T には symbolnumber のキーが含まれることがあり、それらはテンプレートリテラルと互換性がありません。

TYPESCRIPT
type Getters<T> = {
  [K in keyof T & string as `get${Capitalize<K>}`]:
    () => T[K];
};

interface User { name: string; age: number }
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }

type OnlyMethods<T> = {
  [K in keyof T as
    T[K] extends (...args: never[]) => unknown
      ? K : never]: T[K];
};
as による名前変更、never を生成することによるフィルタリング。

見落とされがちな仕様上の細かな点があります: 配列やタプルに適用した場合、準同型なマップ型はキー 01length を持つオブジェクトを生成しません。コンパイラは型の性質を保持します: 要素を変換しつつ、配列やタプルの形を保つのです。これこそが Readonly<[number, string]> がタプルのまま残る理由です。この保存性は、マップ型が準同型でなくなった途端に失われます。例えば、K がキーの任意のユニオンである [P in K] のような場合です。

TYPESCRIPT
type Immutable<T> = {
  readonly [K in keyof T]: T[K];
};

type Pair = Immutable<[number, string]>;
// readonly [number, string]: the tuple survives

type List = Immutable<string[]>;
// readonly string[]
準同型なマップ型は配列とタプルを尊重する。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. キーの再マッピングにおいて、as 節があるキーに対して never を生成すると何が起こりますか?
    • そのプロパティが never 型になる
    • そのキーが結果の型から削除される
    • コンパイラがエラーを出す
  2. { readonly [K in keyof T]: T[K] } をタプル [number, string] に適用すると何が生成されますか?
    • キー 0、1、length を持つオブジェクト
    • readonly [number, string]
    • readonly (number | string)[]
    • コンパイルエラー
  3. マップ型における -? 修飾子の効果は何ですか?
    • プロパティをオプショナルにする
    • オプショナル性と、それが導入した undefined を取り除く
    • readonly 修飾子を取り除く
    • 型からプロパティを削除する