mizkog-logo

Web制作の備忘録

HomeZennPortfolio

Index SignatureとMapped Types

TypeScript

Index Signature

オブジェクトのプロパティを動的に追加したい時に使用する。
「key」の部分はkeyでは無い文言でも問題ないが、慣習的に「key」がよく使用される。

export type User = {
  name: string;
  [key: string]: string;
};

const user: User = {
  name: "テスト",
  account: "test",
}

Index Signatureの欠点

他の型のプロパティがある場合に不都合が起こる。

Index Signatureでstring型を指定しているので、number型が入るとエラーとなる。

export type User = {
  name: string;
  age: number;
  [key: string]: string;
};

const user: User = {
  name: "テスト",
  age: 25,
  account: "test",
}

以下のようにUnion Typesで指定することで回避することが可能。

export type User = {
  name: string;
  age: number;
  [key: string]: string | number;
};

const user: User = {
  name: "テスト",
  age: 25,
  account: "test",
}

ただし、他のプロパティと型が違う場合には色々な型を付与しなければならないのは、Index Signatureの欠点。

存在しないプロパティに対してundefinedを付与して考慮しなければいけない。

以下のように、undefinedを付与することで対処は出来る。

export type User = {
  name: string;
  age: number;
  [key: string]: string | number | undefined;
};

const user: User = {
  name: "テスト",
  age: 25,
  account: "test",
}

ただし、存在するプロパティに対してもundefinedが付与されてしまうため、使い勝手が悪くなってしまう。
そのため、typeof等を使用して絞り込まないとメソッドにもアクセス出来ない。
Index Signatureは便利ではあるが、型がもろくなってしまうので多用するべきではない。

Mapped Types

以下の2点に大きく分類できる。

  • オブジェクトのプロパティ名を限定する場面
  • ジェネリクスと組み合わせて便利な型を作り出す場面

以下の[K in …]の「K」の箇所は何でも良いが、慣習的にKeyの意でKやPropertiesの意でPとすることが多い。

export type User = {
  name: string;
} & PersonalData;

type PersonalData = {
  // height: number;
  // weight: number;
  [K in "height" | "weight"]: number;
}

const user: User = {
  name: "テスト",
  height: 178,
  weight: 65,
}

Mapped Typesのメリット

別の型から参照することが出来る

以下のようにkeyofや更にkeyof tepeofで変数から直接プロパティ名を指定することも出来る。

export type User = {
  name: string;
} & PersonalData;

const foo = {
  height: 200,
  weight: 100,
}

type PersonalData = {
  // height: number;
  // weight: number;
  [K in keyof typeof foo]: number;
}

const user: User = {
  name: "テスト",
  height: 178,
  weight: 65,
}

一括で色々な指定をすることが出来る

以下のようにMapped Typesの後に「?」を付けることでオプショナルにすることが出来るため、****「****weight」が無くても必須ではないのでエラーとならなくなる。

export type User = {
  name: string;
} & PersonalData;

type PersonalData = {
  // height: number;
  // weight: number;
  [K in "height" | "weight"]?: number;
}

const user: User = {
  name: "テスト",
  height: 178,
}

ちなみに、このオプショナルの書き方はIndex Signatureでは出来ないため注意。
以下のようにオブジェクトのような感じでPersonalData[K]とすることで、PersonalDataのnumber等を取り出すことができる。

type PersonalData = {
  height: number;
  weight: number;
  realName: string;
}
type optionalPersonalData = {
  [K in keyof PersonalData]?: PersonalData[K]
  // height: number;
  // weight: number;
}

また、以下のように「-?」とすることで、反対にプロパティを必須にすることが出来る。

type PersonalData = {
  height?: number;
  weight?: number;
  realName?: string;
}
type RiquiredPersonalData = {
  [K in keyof PersonalData]-?: PersonalData[K]
  // height: number;
  // weight: number;
}