lamechang-dev

Webフロントエンドエンジニア lamechangのブログ。

【TypeScript】過剰プロパティチェックの挙動

過剰なプロパティチェック

TypeScriptには、過剰なプロパティチェックを行う機能が存在します。まず、以下の例を見てみましょう。

type Pokemon = {
  ability?: Array<string>;
  weekType: string;
};

const zenigame: Pokemon = {
  // Type '{ abiility: string[]; weekType: string; }' is not assignable to type 'Pokemon'.
  // Object literal may only specify known properties, but 'abiility' does not exist in type 'Pokemon'.Did you mean to write 'ability'?
  abiility: ["bodyAttack", "waterGun"],
  weekType: "denki",
};

オブジェクト zenigameは型 Pokemonを期待しているので、前回の記事の通り、そのサブタイプを渡してあげればよさそうです。

ここで、私はzenigame のプロパティ名を1つ間違えてしまいました。abiility はタイポしてますね。javascriptではこういったバグよく起こりますよね。

これは型エラーになるかなぁと一瞬思うも、しかし、よく見ると型Pokemonのプロパティabilityは型が Array<string> | undefinedであるので、プロパティabilityzenigameにないことは abilityundefinedであることと同値であり、これはArray<string> | undefinedのサブタイプなので条件を満たします。

なので、タイポはしたものの zenigameはサブタイプの条件を満たしているように見えますね。なぜTypeScriptはこのバグを検知することができたのでしょうか?

この挙動こそ、過剰プロパティチェックです。オブジェクトリテラルからTypeScriptが推論を行う型に関して適用されます。フレッシュなオブジェクトリテラル型 Aを別の型 Bに割り当てる際に、Bには存在していないプロパティがAに存在している場合、TypeScript はエラーを吐きます。

仮にそのオブジェクトリテラルが型アサーションを使用しているか、変数に割り当てられているのであればこの挙動は適用されません。具体的に以下の例を見てみましょう。

過剰プロパティチェックが適用されないケース

変数に割り当てられる際の例

type Pokemon = {
  ability?: Array<string>;
  weekType: string;
};

// オブジェクトリテラル型は代入を通して通常のオブジェクト型に拡大される
const zenigame = {
  abiility: ["bodyAttack", "waterGun"],
  weekType: "denki",
};

// OK
const pokemonA: Pokemon = zenigame;

アサーションの例

type Pokemon = {
  ability?: Array<string>;
  weekType: string;
};

// OK 
const zenigame: Pokemon = {
  abiility: ["bodyAttack", "waterGun"],
  weekType: "denki",
} as Pokemon; // asによる型アサーション。オブジェクトの型がPokemonであることをTypeScriptに主張する

と言った感じで、フレッシュでないオブジェクトリテラル については過剰プロパティチェックの挙動は生まれません。

終わり。