やあ。TypeScriptの深淵へようこそ。
現場でコードを書いていて、「なぜかバグが消えない」「APIのレスポンスが空なのか、キー自体がないのか判別がつかない」なんて経験はないかな? その迷宮の入り口、実は `tsconfig.json` の設定ひとつで封鎖できるんだ。
今日は、中級者エンジニアなら絶対に押さえておくべき `exactOptionalPropertyTypes` について、現場の泥臭い話を交えながら深掘りしていこう。
—
TypeScriptの「オプショナル」という名の甘い罠
TypeScriptのデフォルト設定では、オプショナルプロパティ(`prop?: string`)に対して、`undefined` を明示的に代入することが許されている。
type User = {
name?: string;
};
const user: User = { name: undefined }; // これ、通ってしまうんだよね
パッと見、「別にいいじゃん」と思うかもしれない。だが、これが地獄の始まりだ。`Object.keys(user)` を叩いてみると、`[‘name’]` が返ってくる。つまり、「値は入っていないが、キーは存在する」状態だ。
JSONに変換してサーバーに投げたとき、`{ “name”: undefined }` というデータが送られ、バックエンドのバリデーションで「型不一致」として弾かれる。あるいは、`{ name: undefined }` と `{ }` を別物として扱う処理系で事故が起きる。
この「存在しない」ことと「値が空である」ことの境界を曖昧にするのが、デフォルトのTypeScriptの優しさであり、最大の罠なんだ。
`exactOptionalPropertyTypes` がもたらす「厳格な境界線」
`tsconfig.json` で `exactOptionalPropertyTypes: true` を有効にすると、TypeScriptは「オプショナルなフィールドに `undefined` を代入すること」を許さなくなる。
{
“compilerOptions”: {
“exactOptionalPropertyTypes”: true
}
}
これをオンにすると、先ほどのコードは型エラーを吐く。
type User = {
name?: string;
};
// コンパイルエラー!
// Type ‘undefined’ is not assignable to type ‘string | undefined’ with exactOptionalPropertyTypes enabled.
const user: User = { name: undefined };
「おっと、じゃあどうすればいいの?」と思うだろう。答えはシンプルだ。「そのプロパティをオブジェクトから消去する」か、「最初からオプショナルにしない(`string | undefined` を明示する)」かの二択を迫られることになる。
現場でどう使い分けるべきか:ベストプラクティス
現場でこの設定を有効にすると、既存のコードでいくつかエラーが出るはずだ。そのとき、ただ闇雲に修正するのではなく、以下の指針で書き換えてみてほしい。
1. データの「存在有無」を厳密に制御したい場合
プロパティ自体を消す(`delete` やスプレッド演算子を使う)のが正解だ。
type Settings = {
theme?: ‘light’ | ‘dark’;
};
// 良い例:プロパティ自体を存在させない
const getSettings = (theme?: ‘light’ | ‘dark’): Settings => {
if (!theme) return {}; // これが最も安全
return { theme };
};
2. 「未定義であること」を値として扱いたい場合
オプショナルではなく、ユニオン型で明示する。これが最も意図が明確だ。
type UserProfile = {
// undefinedを許容するなら、明確に型に書く
bio: string | undefined;
};
const profile: UserProfile = { bio: undefined }; // これはエラーにならない
ブラウザ(JSエンジン)の裏側を想像する
なぜここまで厳密にする必要があるのか? それは、JavaScriptという言語が「存在しないキーへのアクセス」に対して非常に寛容だからだ。
`obj.name` にアクセスしたとき、キーが定義されていなければ `undefined` が返る。だが、キーが定義されていて値が `undefined` な場合も、やはり `undefined` が返る。プログラマは、「なぜ値が取れないのか(データがないからなのか、取得に失敗したのか)」を推測しなければならなくなる。
`exactOptionalPropertyTypes` は、TypeScriptのコンパイル段階でこの「曖昧な状態」を排除し、ランタイムでの予期せぬ `undefined` 混入を未然に防ぐための強力なガードレールなんだ。
最後に:一歩先のエンジニアへ
このオプションを有効にすると、コードの修正量は増えるかもしれない。しかし、その苦労は間違いなく「将来の自分」を救うことになる。
特に外部APIと連携するフロントエンド開発において、「データが存在するかどうか」を型レベルで担保できることは、防御的プログラミングの第一歩だ。
今日から `tsconfig.json` にこの1行を書き加えてみてほしい。最初はエラーに追いかけられるかもしれないが、それを乗り越えた先には、驚くほど堅牢で美しいデータフローが待っているはずだ。
型定義は、ただの制約じゃない。君が書くコードの「仕様書」であり「設計図」なんだ。誇りを持って、厳格に書こう。応援しているよ。

コメント