is possibly 'undefined' の原因と直し方
interfaceで?を付けたオプショナルプロパティは、値が入っていない(undefined)可能性を型として保持します。narrowing(存在確認)をせずにメソッドやプロパティへアクセスすると、このエラーになります。
エラーメッセージの読み方
main.ts(5,10): error TS18048: 'p.bio' is possibly 'undefined'.
main.ts- ファイル名
5- 行番号
10- 列番号 — この位置でtscが検査に失敗しました
TS18048- エラーコード — 検索するとTypeScriptの解説が見つかります
'p.bio' is possibly 'undefined'.- 内容 — 期待していたもの、または受け付けられなかったもの
このエラーが出る典型パターン
パターン1
1 interface Profile { 2 bio?: string; 3 } 4 function shout(p: Profile): string { 5 return p.bio .toUpperCase(); ^ 6 } 7 console.log(shout({ bio: "hi" }));
bioはオプショナルプロパティなのでundefinedかもしれません。narrowingせずにtoUpperCase()を呼ぶとエラーになります。
直し方: bio を bio! にします。
パターン2
1 interface Cart { 2 coupon?: string; 3 } 4 function applyCoupon(c: Cart): number { 5 return c.coupon .length; ^ 6 } 7 console.log(applyCoupon({ coupon: "SAVE10" }));
couponもオプショナルなのでundefinedの可能性があります。lengthへアクセスする前に確認が必要です。
直し方: coupon を coupon! にします。
パターン3
1 interface Options { 2 timeout?: number; 3 } 4 function describe(o: Options): number { 5 return o.timeout + 1; ^ 6 } 7 console.log(describe({ timeout: 9 }));
timeoutはオプショナルなnumberです。undefinedかもしれない値に対して+演算はできません。
直し方: timeout を timeout! にします。
パターン4
1 interface Account { 2 nickname?: string; 3 } 4 function shout(a: Account): string { 5 return a.nickname .toUpperCase(); ^ 6 } 7 console.log(shout({ nickname: "al" }));
nicknameはオプショナルプロパティなのでundefinedかもしれません。narrowingせずにtoUpperCase()を呼ぶとエラーになります。
直し方: nickname を nickname! にします。
パターン5
1 interface Settings { 2 retries?: number; 3 } 4 function describe(s: Settings): number { 5 return s.retries + 1; ^ 6 } 7 console.log(describe({ retries: 3 }));
retriesはオプショナルなnumberです。undefinedかもしれない値に対して+演算はできません。
直し方: retries を retries! にします。
よくある誤解
「オプショナルプロパティも、実際に値を渡していれば安全なはず」という考えは、TypeScriptの型チェックには通用しません。tscは呼び出し元の実際の値ではなく、型として「undefinedかもしれない」という可能性だけを見て機械的に判定します。
実務での勘所
narrowingが効くのは、確認した直後、同じ変数への参照が保証されている範囲だけです。if (obj.prop) { ... }のように直接プロパティを確認した場合はその中でobj.propの型が絞り込まれますが、途中で何か別の関数を呼んだり、objが実はgetterでアクセスのたびに違う値を返す可能性がある場合、TypeScriptは絞り込みを維持できず、undefinedの可能性がある型に戻ってしまうことがあります。確認した変数をいったんconstのローカル変数に取り出してから使うと、この種の絞り込みの喪失を避けやすくなります。