メインコンテンツまでスキップ

exactOptionalPropertyTypes

exactOptionalPropertyTypesはオプションプロパティにundefinedの代入することを禁止するコンパイラオプションです。

  • デフォルト: false
  • 追加されたバージョン: 4.4
  • TypeScript公式が有効化推奨

解説

今までオプション修飾子は値を設定しないことに加えてundefinedを意図的に設定することができました。

ts
interface User {
name: string;
nationality?: "India" | "China";
}
 
const user1: User = {
name: "Srinivasa Aiyangar Ramanujan",
nationality: "India",
};
 
const user2: User = {
name: "Sergei Vasilevich Rachmaninov",
nationality: undefined,
};
 
const user3: User = {
name: "Yekaterina II Alekseyevna",
};
ts
interface User {
name: string;
nationality?: "India" | "China";
}
 
const user1: User = {
name: "Srinivasa Aiyangar Ramanujan",
nationality: "India",
};
 
const user2: User = {
name: "Sergei Vasilevich Rachmaninov",
nationality: undefined,
};
 
const user3: User = {
name: "Yekaterina II Alekseyevna",
};

値が未定義であることと値がundefinedであることは厳密には動作が異なります。たとえばObject.keys()は最たる例で、上記のuser1, user2, user3にそれぞれObject.keys()を適用すれば結果は次のようになります。

ts
// user1
["name", "nationality"];
// user2
["name", "nationality"];
// user3
["name"];
ts
// user1
["name", "nationality"];
// user2
["name", "nationality"];
// user3
["name"];

この差異が意図しない実行時エラーを生むことがあります。意図する値が設定されていれば(この場合'India' | 'China')nationalityObject.keys()に含まれるべきですがundefinedのときは結局その先で値の存在チェックが必要になります。

このオプションを有効にするとinterface, typeでオプション修飾子を持つキーはその値がキー自体を持たないようにしなければなりません。先ほどの例ではundefinedを代入したuser2で次のようなエラーが発生します。

ts
interface User {
name: string;
nationality?: "India" | "China";
}
 
const user1: User = {
name: "Srinivasa Aiyangar Ramanujan",
nationality: "India",
};
 
const user2: User = {
Type '{ name: string; nationality: undefined; }' is not assignable to type 'User' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Types of property 'nationality' are incompatible. Type 'undefined' is not assignable to type '"India" | "China"'.2375Type '{ name: string; nationality: undefined; }' is not assignable to type 'User' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Types of property 'nationality' are incompatible. Type 'undefined' is not assignable to type '"India" | "China"'.
name: "Sergei Vasilevich Rachmaninov",
nationality: undefined,
};
 
const user3: User = {
name: "Yekaterina II Alekseyevna",
};
ts
interface User {
name: string;
nationality?: "India" | "China";
}
 
const user1: User = {
name: "Srinivasa Aiyangar Ramanujan",
nationality: "India",
};
 
const user2: User = {
Type '{ name: string; nationality: undefined; }' is not assignable to type 'User' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Types of property 'nationality' are incompatible. Type 'undefined' is not assignable to type '"India" | "China"'.2375Type '{ name: string; nationality: undefined; }' is not assignable to type 'User' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Types of property 'nationality' are incompatible. Type 'undefined' is not assignable to type '"India" | "China"'.
name: "Sergei Vasilevich Rachmaninov",
nationality: undefined,
};
 
const user3: User = {
name: "Yekaterina II Alekseyevna",
};

どうしてもキーにundefinedも指定したい場合はオプション修飾子に加えてundefinedのユニオン型を付加してください。