こんにちは、かつコーチです。
前回は、よくあるTypeScriptエラーの読み方について解説しました。
今回は少し視点を変えて、そもそもTypeScriptが「どこまで厳しくエラーを検知するか」を決めているtsconfig.jsonのstrictオプションを掘り下げていきます。
同じTypeScriptのコードでも、設定次第でエラーになったりならなかったりします。
この違いを理解しておくと、「なぜこの現場のコードは型エラーがやたら多いんだろう」「なぜこのプロジェクトはanyだらけでも動くんだろう」といった疑問が解消されるはずです。
strictモードとは何か
型チェックの厳しさをまとめて切り替えるスイッチ
strictモードとは、TypeScriptの型チェックを厳格にするための複数のオプションを、まとめてオンにするための設定です。
tsconfig.jsonに次のように書くだけで、後述する複数の厳格化オプションが一括で有効になります。
{
"compilerOptions": {
"strict": true
}
}
strict: trueは、実質的に「TypeScriptの型チェックを一番安全な状態で使う」という宣言だと考えてください。
公式のスターターテンプレートやフレームワークのTypeScript構成でも、最近はデフォルトでtrueになっていることがほとんどです。
なぜstrictモードが重要なのか
TypeScriptは、実は初期設定のままだと「かなり緩い」型チェックしか行いません。
strictをオフのままにしておくと、次のようなコードでもエラーになりません。
function greet(name) { // 引数nameの型が暗黙的にany
return "こんにちは、" + name;
}
greet(123); // 数値を渡してもエラーにならない
型を書いているつもりでも、実はTypeScriptの恩恵をほとんど受けられていない、という状態になりがちです。
私が初めて既存プロジェクトのtsconfig.jsonを確認したとき、strictがfalseのままになっていて、想定していたより型チェックが緩く、思わぬ箇所でundefinedエラーが実行時に発生していたことがありました。
そのときに初めて「TypeScriptを使っている」ことと「TypeScriptの型安全性を活かせている」ことは別物なのだと痛感しました。
strictモードに含まれる主要オプション
noImplicitAny:暗黙のanyを禁止する
noImplicitAnyは、型注釈を省略した引数や変数が、暗黙的にany型(何でも入る型)になることを禁止するオプションです。
❌ Before:型注釈を省略して暗黙のanyになっている
function add(a, b) { // a, bの型が暗黙的にany
return a + b;
}
add("1", 2); // 文字列と数値を足しても実行時までエラーに気づけない
✅ After:明示的に型注釈を書く
function add(a: number, b: number): number {
return a + b;
}
add(1, 2); // OK
add("1", 2); // コンパイル時にエラーとして検知できる
noImplicitAnyが有効だと、型注釈を書き忘れた時点でエラーとして教えてくれるため、「書き忘れ」を仕組みで防げるようになります。
strictNullChecks:nullとundefinedの扱いを厳格にする
strictNullChecksは、nullやundefinedを「その型に含まれる特別な値」として厳しく扱うオプションです。
type User = {
name: string;
};
function getUser(id: number): User | undefined {
const users: User[] = [{ name: "かつコーチ" }];
return users[id];
}
const user = getUser(0);
console.log(user.name); // エラー:userはundefinedかもしれない
strictNullChecksが無効な環境では、このコードは何の警告もなく通ってしまい、実行時に「Cannot read properties of undefined」というエラーで初めて気づく、ということが起こります。
有効にしておけば、コンパイルの時点で「userはundefinedの可能性がある」と教えてもらえるため、実行前にバグの芽を摘めます。
strictFunctionTypes:関数の型の互換性を厳密にする
strictFunctionTypesは、関数を別の関数型の変数に代入するときの型チェックを厳密にするオプションです。
やや専門的な内容なので、初中級のうちは「関数を引数として渡すときの型チェックがより厳しくなる」くらいの理解で十分です。
type Handler = (event: MouseEvent) => void;
// より広い型(Event)を受け取る関数を、より狭い型(MouseEvent)の場所に代入しようとすると
// strictFunctionTypesが無効な場合は見逃されやすい不整合を検知してくれる
const handleClick: Handler = (event: Event) => {
console.log(event);
};
コールバック関数を多用するイベント処理などで、意図しない型のすれ違いを防いでくれるオプションです。
strictPropertyInitialization:クラスのプロパティ初期化を厳格にする
strictPropertyInitializationは、クラスのプロパティが確実に初期化されているかをチェックするオプションです。
class UserProfile {
name: string; // エラー:初期化されていない
age: number;
constructor(age: number) {
this.age = age;
// nameを初期化し忘れている
}
}
strictNullChecksと併用することで効果を発揮するオプションで、コンストラクタ内でプロパティの初期化漏れがあると教えてくれます。
tsconfig.jsonのその他のよく使うオプション
target:出力するJavaScriptのバージョンを指定する
{
"compilerOptions": {
"target": "ES2020"
}
}
targetは、コンパイル後のJavaScriptがどのバージョンの構文で出力されるかを指定するオプションです。
対応が必要なブラウザやNode.jsのバージョンに合わせて選びます。
迷ったら、モダンな環境を前提にES2020以降を指定しておけば、実務ではほぼ困りません。
esModuleInterop:モジュールの読み込み方式を統一する
{
"compilerOptions": {
"esModuleInterop": true
}
}
esModuleInteropは、CommonJS形式(require)とES Modules形式(import)の互換性を高めてくれるオプションです。
これがfalseのままだと、一部のライブラリをimportしたときに「うまく読み込めない」という分かりにくいエラーに遭遇することがあります。
私も昔、あるライブラリをimportしたら実行時にundefined扱いになってしまい、原因を探るのに時間を使ったことがありますが、原因はこのesModuleInteropが無効だったことでした。
現在のプロジェクトテンプレートでは基本的にtrueが初期値になっているので、意識することは少なくなりましたが、古いプロジェクトを触るときは確認しておくと安心です。
strictモードは最初から有効にすべきか
新規プロジェクトでは迷わずtrueにする
これから新しく始めるプロジェクトであれば、strict: trueを最初から設定しておくことを強くおすすめします。
途中からstrictを有効にすると、それまで見逃されていたエラーが一気に大量発生し、修正が大変になるからです。
最初から厳しくしておいた方が、結果的に学習の効率も上がります。
既存プロジェクトでは段階的に導入する
すでにstrict: falseで運用されている大規模なプロジェクトの場合、いきなりtrueにすると膨大なエラーが出てしまうことがあります。
そうしたケースでは、strict全体を一気に有効化するのではなく、noImplicitAnyだけ先に有効にする、といった段階的な移行が現実的です。
{
"compilerOptions": {
"strict": false,
"noImplicitAny": true
}
}
まずは影響範囲の小さいオプションから1つずつオンにしていき、エラーを潰しながら少しずつ厳しくしていくのが実務での定石です。
まとめ
この記事のポイント
strict: trueは、複数の厳格な型チェックオプションをまとめて有効にする設定noImplicitAnyやstrictNullChecksは、それぞれ「暗黙のany」「null/undefinedの見落とし」を防いでくれる- 新規プロジェクトは最初から
strict: true、既存プロジェクトは段階的な移行がおすすめ targetやesModuleInteropなど、strict以外の基本オプションも合わせて押さえておく
次に読むべき記事
strictモードを理解したところで、次は多くの人がつまずく「とりあえずany」からの卒業について解説していきます。
→ 次の記事:「とりあえずany」から卒業するための考え方