Code Fix

中級

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" }));
main.ts(5,10): error TS18048: 'p.bio' is possibly 'undefined'.

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" }));
main.ts(5,10): error TS18048: 'c.coupon' is possibly 'undefined'.

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 }));
main.ts(5,10): error TS18048: 'o.timeout' is possibly 'undefined'.

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" }));
main.ts(5,10): error TS18048: 'a.nickname' is possibly 'undefined'.

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 }));
main.ts(5,10): error TS18048: 's.retries' is possibly 'undefined'.

retriesはオプショナルなnumberです。undefinedかもしれない値に対して+演算はできません。

直し方: retries を retries! にします。

この問題を解いてみる →

よくある誤解

「オプショナルプロパティも、実際に値を渡していれば安全なはず」という考えは、TypeScriptの型チェックには通用しません。tscは呼び出し元の実際の値ではなく、型として「undefinedかもしれない」という可能性だけを見て機械的に判定します。

実務での勘所

narrowingが効くのは、確認した直後、同じ変数への参照が保証されている範囲だけです。if (obj.prop) { ... }のように直接プロパティを確認した場合はその中でobj.propの型が絞り込まれますが、途中で何か別の関数を呼んだり、objが実はgetterでアクセスのたびに違う値を返す可能性がある場合、TypeScriptは絞り込みを維持できず、undefinedの可能性がある型に戻ってしまうことがあります。確認した変数をいったんconstのローカル変数に取り出してから使うと、この種の絞り込みの喪失を避けやすくなります。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)