🐧

TypeScript 5.7 Beta の 初期化されていない変数チェックの改善を正しく理解する

2024/10/14に公開

概要

https://devblogs.microsoft.com/typescript/announcing-typescript-5-7-beta/ でTypeScript5.7のBetaが発表されました。そのうちの機能に、初期化されていない変数チェックの改善が含まれていましたが、例を読んだ時に自分が誤解してしまった内容があったため記事にしました。

準備

npm install -D typescript@beta

を実行し、package.jsonにtypescriptの5.7.0-betaを追加します。

{
  "devDependencies": {
    "typescript": "^5.7.0-beta"
  }
}

そして、index.tsファイルを作成し、

npx tsc --init // tsconfig.jsonを作成
npx tsc

でコンパイルが行えます。

変更点

従来のバージョンでも変数の初期化が行われていない時には、以下のようにコンパイル時にエラーとなりました。

index.ts
function someCondition() {
  return Math.random() < 0.5;
}
function doSomeWork() {
  return 1;
}

let result: number;
if (someCondition()) {
  result = doSomeWork();
} else {
  let temporaryWork = doSomeWork();
  temporaryWork *= 2;
  // forgot to assign to 'result'
}

console.log(result);
typescript 5.6
> npx tsc      
index.ts:43:13 - error TS2454: Variable 'result' is used before being assigned.

43 console.log(result);
               ~~~~~~


Found 1 error in index.ts:43

しかし、以下のように初期化されていない変数が別の関数で使用された場合には今までコンパイル時にエラーが発生しませんでした。

index.ts
function foo() {
  let result: number;

  // do work, but forget to assign to 'result'

  function printResult() {
    console.log(result.toString());
  }
  printResult();
}

foo();

最新のtypescript5.6.3では上記のコードは問題なくコンパイルできますが、resultはundefinedとなるため、ランタイムでエラーが発生してしまいます。

typescript 5.6
> npx tsc      
> node index.js
/Users/hoge/dev/typescript-difference/typescript-5.6/index.js:24
        console.log(result.toString());
                           ^

TypeError: Cannot read properties of undefined (reading 'toString')
    at printResult (/Users/hoge/dev/typescript-difference/typescript-5.6/index.js:24:28)
    at foo (/Users/hoge/dev/typescript-difference/typescript-5.6/index.js:26:5)
    at Object.<anonymous> (/Users/hoge/dev/typescript-difference/typescript-5.6/index.js:28:1)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47

一方、5.7のbeta版ではresultが初期化されていないためコンパイル時点でエラーとなります。

typescript 5.7 beta
> npx tsc
index.ts:33:17 - error TS2454: Variable 'result' is used before being assigned.

33     console.log(result.toString());
                   ~~~~~~


Found 1 error in index.ts:33

では、ブログの例にあるような以下の場合はどうでしょうか?

index.ts
function foo() {
  let result: number;
  if (someCondition()) {
    result = doSomeWork();
  } else {
    let temporaryWork = doSomeWork();
    temporaryWork *= 2;
    // forgot to assign to 'result'
  }

  printResult();

  function printResult() {
    console.log(result.toString()); // no error here.
  }
}

foo();

この場合、従来のバージョンでも5.7のbeta版でもコンパイル時にエラーとなりません

Unfortunately, there are some places where this analysis doesn’t work. For example, if the variable is accessed in a separate function, the type system doesn’t know when the function will be called, and instead takes an "optimistic" view that the variable will be initialized.

While TypeScript 5.7 is still lenient on variables that have possibly been initialized, the type system is able to report errors when variables have never been initialized at all.

とあるように、今回の変更では変数の初期化が全く行われていない時にエラーとなるようになっただけであり、条件分岐の一部で初期化が行われていた場合には「楽観的に」初期化されたものとみなされ、エラーにはならないので注意しましょう。

Discussion