【実務・中級編】 tsconfigのexactOptionalPropertyTypesオプション – TypeScript実践ガイド

やあ。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行を書き加えてみてほしい。最初はエラーに追いかけられるかもしれないが、それを乗り越えた先には、驚くほど堅牢で美しいデータフローが待っているはずだ。

型定義は、ただの制約じゃない。君が書くコードの「仕様書」であり「設計図」なんだ。誇りを持って、厳格に書こう。応援しているよ。

コメント

タイトルとURLをコピーしました