※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。


【TypeScript】inを使った構文まとめ

Mapped Types TypeScript in keyof 型エイリアス

投稿日:2021年4月3日

このエントリーをはてなブックマークに追加
この記事ではTypeScriptでのinを使った構文について詳しく解説しています。

はじめに

この記事について

この記事ではTypeScriptでのinを使った構文について解説しています。

環境

この記事のコードは以下の環境で確認されました。

  • typescript version 4.2.3

実践

プロパティの存在確認

{prop} in {object}と書くことで{prop}というプロパティが{object}というオブジェクトに存在するかどうかを確認することができます。
返し値はboolean型で存在する場合にはtrue、存在しない場合にはfalseが返ります。

// 型の定義
type User = {
  name: string;
  age: number;
  email: string;
};

// オブジェクトを作成
const user: User = {
  name: "marsquai",
  age: 26,
  email: "example@marsquai.com",
};

// keyの確認
if ("name" in user) {
  console.log("Found 'name' key in object keys");
} else {
  console.log("Not found.");
}
出力
Found 'name' key in object keys

この使い方はJavaScriptにもありますね。JavaScriptの場合と同様に{Index} in {Array}という使い方をすることもできます。その場合、{Array}の指定した{index}に値が存在するかどうかをboolean型で受け取ることができます。

// 様々な値が入った配列
const sampleArray: Array<any> = [1, "marsquai", 3, undefined, null];

// 配列の一部を削除
delete sampleArray[2];

// 配列のサイズ+1までをinで比較
for (let index = 0; index <= sampleArray.length; index++) {
  if (index in sampleArray) {
    console.log(`${index}:${sampleArray[index]}[TRUE]`);
  } else {
    console.log(`${index}:${sampleArray[index]}[FALSE]`);
  }
}
出力
0:1[TRUE]
1:marsquai[TRUE]
2:undefined[FALSE]
3:undefined[TRUE]
4:null[TRUE]
5:undefined[FALSE]

deleteで値を削除したindex=2の時と、配列のサイズ外のindex=5の時に結果がfalseになっていることは説明の必要は無いと思います。


注意してほしいのは、index=3のときの結果です。Arrayから取得できる値はindex=2,5と同じundefinedですが、in構文の結果はtrueになっています。つまり、in構文が確認しているのは値がundefinedであるかどうかでは無いということです。
JavaScript(TypeScript)においてundefinedは値が定義されていないことを意味します。しかし、今回の様にArrayundefinedを渡した場合には定義されていない値が存在するという意味になるのです。

他者の作ったライブラリなどを使っている場合に、このあたりをあやふやにしておくと思わぬバグを作ってしまいます。

Mapped Type

2つ目はTypeScript独自の使い方です。
TypeScriptにはkeyof Tという構文があります。これはTで指定した型に許容されたプロパティ名の型を生成する構文です。これと組み合わせて[P in keyof T]と書くことでTのプロパティを元に新たなプロパティを生成できます。

これを使うことで既存の型を柔軟に変換できます。

// ユーザー
type User = {
  name: string;
  age: number;
  email: string;
};

/**
 * プロパティが読み取り専用のユーザー
 * 以下と同じ
 * type PartialUser = {
 * name?: string;
 * age?: number;
 * email?: string;
 * }
 */
type PartialUser = {
  [P in keyof User]?: User[P];
};

/**
 * プロパティが読み取り専用のユーザー
 * 以下と同じ
 * type ReadOnlyUser = {
 * readonly name: string;
 * readonly age: number;
 * readonly email: string;
 * }
 */
type ReadOnlyUser = {
  readonly [P in keyof User]: User[P];
};

/**
 * 値がbooleanのユーザー
 * 以下と同じ
 * type BooleanUser = {
 * name: boolean;
 * age: boolean;
 * email: boolean;
 * 
 */
type BooleanOnlyUser = {
  [P in keyof User]: boolean;
};

ちなみに公式ドキュメントにもあるとおりジェネリック型の関数として定義することで、より汎用性の高いコードを書くことができます。

// ユーザー
type User = {
    name: string;
    age: number;
    email: string;
  };
  
  /**
   * プロパティが読み取り専用のオブジェクト
   * @param T {type} 型
   */
  type PartiaObj<T> = {
    [P in keyof T]?: T[P];
  };

  const partialUser: PartiaObj<User> = {
      name: "marsquai"
  }

さいごに

参考URL

このエントリーをはてなブックマークに追加


関連記事

記事へのコメント