複雑な Web アプリケーションを構築している場合、プログラミング言語として TypeScript を選択することになるでしょう。 TypeScript は、強力な型システムと静的分析機能で広く愛されており、コードが堅牢でエラーがないことを保証するための強力なツールとなっています。 また、コード エディターとの統合により開発プロセスが高速化され、開発者がより効率的にコードを操作し、より正確なヒントやオートコンプリートを取得できるようになり、大量のコードの安全なリファクタリングが可能になります。 コンパイラーは TypeScript の中心であり、型の正確性をチェックし、TypeScript コードを JavaScript に変換します。ただし、TypeScript の機能を最大限に活用するには、コンパイラーを正しく構成することが重要です。 各 TypeScript プロジェクトには、コンパイラーのすべての構成オプションを保持する 1 つ以上の ファイルがあります。 tsconfig.json tsconfig の構成は、TypeScript プロジェクトで最適なタイプ セーフティと開発者エクスペリエンスを実現するための重要な手順です。関連するすべての重要な要素を時間をかけて慎重に検討することで、開発プロセスをスピードアップし、コードが堅牢でエラーがないことを保証できます。 標準構成の欠点 tsconfig のデフォルト構成により、開発者は TypeScript の利点の大部分を逃す可能性があります。これは、多くの強力な型チェック機能が有効にならないためです。 「デフォルト」構成とは、型チェック コンパイラ オプションが設定されていない構成を意味します。 例えば: { "compilerOptions": { "target": "esnext", "module": "esnext", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, }, "include": ["src"] } いくつかの主要な構成オプションがないと、主に 2 つの理由でコードの品質が低下する可能性があります。まず、TypeScript のコンパイラは、さまざまな場合に および 型を誤って処理する可能性があります。 null undefined 次に、 型がコードベースに制御不能に出現する可能性があり、この型に関する型チェックが無効になる可能性があります。 any 幸いなことに、これらの問題は、構成内のいくつかのオプションを調整することで簡単に修正できます。 ストリクトモード { "compilerOptions": { "strict": true } } Strict モードは、幅広い型チェック動作を有効にすることで、プログラムの正確性をより強力に保証する必須の構成オプションです。 tsconfig ファイルで厳密モードを有効にすることは、最大限の型安全性とより良い開発者エクスペリエンスを実現するための重要なステップです。 tsconfig の構成には少し手間がかかりますが、プロジェクトの品質を向上させるのに大いに役立ちます。 コンパイラ オプションは、特に 、 、 の strict モード ファミリ オプションをすべて有効にします。 strict noImplicitAny strictNullChecks strictFunctionTypes これらのオプションは個別に設定することもできますが、いずれかをオフにすることはお勧めできません。その理由を例を見てみましょう。 暗黙的な任意の推論 { "compilerOptions": { "noImplicitAny": true } } 型は静的型システムの危険な抜け穴であり、これを使用するとすべての型チェック ルールが無効になります。その結果、バグが見逃されたり、コード エディターのヒントが適切に機能しなくなったりするなど、TypeScript のすべての利点が失われます。 any 使用しても問題がないのは、極端な場合またはプロトタイピングが必要な場合のみです。最善の努力にもかかわらず、 型が暗黙的にコードベースに侵入する可能性があります。 any any デフォルトでは、コンパイラは、 ベースにエラーが発生しても、多くのエラーを許容します。具体的には、TypeScript を使用すると、型が自動的に推論できない場合でも、変数の型を指定しなくても済みます。 any 問題は、関数の引数などに変数の型を指定することをうっかり忘れてしまう可能性があることです。 TypeScript はエラーを表示する代わりに、変数の型を として自動的に推測します。 any function parse(str) { // ^? any return str.split(''); } // TypeError: str.split is not a function const res1 = parse(42); const res2 = parse('hello'); // ^? any コンパイラ オプションを有効にすると、コンパイラは変数の型が として自動的に推論されるすべての場所を強調表示します。この例では、TypeScript は関数の引数の型を指定するように求めます。 noImplicitAny any function parse(str) { // ^ Error: Parameter 'str' implicitly has an 'any' type. return str.split(''); } 型を指定すると、TypeScript は文字列パラメーターに数値を渡す際のエラーをすぐに検出します。変数 に格納される関数の戻り値も正しい型になります。 res2 function parse(str: string) { return str.split(''); } const res1 = parse(42); // ^ Error: Argument of type 'number' is not // assignable to parameter of type 'string' const res2 = parse('hello'); // ^? string[] Catch 変数の不明な型 { "compilerOptions": { "useUnknownInCatchVariables": true } } を構成すると、try-catch ブロックで例外を安全に処理できるようになります。デフォルトでは、TypeScript は catch ブロック内のエラー タイプが であると想定するため、エラーに対して何でもできるようになります。 useUnknownInCatchVariables any たとえば、キャッチしたエラーをそのまま、 のインスタンスを受け入れるログ関数に渡すことができます。 Error function logError(err: Error) { // ... } try { return JSON.parse(userInput); } catch (err) { // ^? any logError(err); } ただし、実際には、エラーのタイプについての保証はなく、実行時にエラーが発生したときにのみ、その真のタイプを判断できます。ロギング関数が ではないものを受信すると、実行時エラーが発生します。 Error したがって、 オプションは、エラーの種類を 」から に切り替えて、何かを行う前にエラーの種類を確認するように促します。 useUnknownInCatchVariables any unknown try { return JSON.parse(userInput); } catch (err) { // ^? unknown // Now we need to check the type of the value if (err instanceof Error) { logError(err); } else { logError(new Error('Unknown Error')); } } TypeScript は、 関数に渡す前に 変数の型を確認するように求めるプロンプトを表示するため、より正確で安全なコードが得られます。残念ながら、このオプションは、 関数またはコールバック関数での入力エラーには役に立ちません。 logError err promise.catch() ただし、 ような場合の対処方法については、次の記事で説明します。 any Call メソッドと apply メソッドの型チェック { "compilerOptions": { "strictBindCallApply": true } } 別のオプションは、 および を介した 内呼び出しの外観を修正します。これは最初の 2 つのケースほど一般的ではありませんが、考慮することが重要です。デフォルトでは、TypeScript はそのような構造の型をまったくチェックしません。 call apply any たとえば、関数の引数として何でも渡すことができ、最終的には常に 型を受け取ることになります。 any function parse(value: string) { return parseInt(value, 10); } const n1 = parse.call(undefined, '10'); // ^? any const n2 = parse.call(undefined, false); // ^? any オプションを有効にすると、TypeScript がよりスマートになるため、戻り値の型が として正しく推論されます。また、間違った型の引数を渡そうとすると、TypeScript はエラーを指摘します。 strictBindCallApply number function parse(value: string) { return parseInt(value, 10); } const n1 = parse.call(undefined, '10'); // ^? number const n2 = parse.call(undefined, false); // ^ Argument of type 'boolean' is not // assignable to parameter of type 'string'. 実行コンテキストの厳密な型 { "compilerOptions": { "noImplicitThis": true } } プロジェクト内で 出現を防ぐのに役立つ次のオプションは、関数呼び出しでの実行コンテキストの処理を修正します。 JavaScript の動的性質により、関数内のコンテキストのタイプを静的に決定することが困難になります。 any デフォルトでは、TypeScript はそのような場合にコンテキストに 型を使用し、警告を出しません。 any class Person { private name: string; constructor(name: string) { this.name = name; } getName() { return function () { return this.name; // ^ 'this' implicitly has type 'any' because // it does not have a type annotation. }; } } コンパイラ オプションを有効にすると、関数のコンテキストの種類を明示的に指定するように求められます。このようにして、上記の例では、 クラスの フィールドではなく関数コンテキストにアクセスした際のエラーを捕捉できます。 noImplicitThis Person name TypeScript での Null および未定義のサポート { "compilerOptions": { "strictNullChecks": true } } 次に、 モードに含まれるいくつかのオプションでは、コードベースに 型が表示されません。ただし、TS コンパイラーの動作がより厳密になり、開発中により多くのエラーが見つかる可能性があります。 strict any 最初のこのようなオプションは、TypeScript での と の処理を修正します。デフォルトでは、TypeScript は と どの型に対しても有効な値であると想定するため、予期しない実行時エラーが発生する可能性があります。 null undefined null undefined コンパイラ オプションを有効にすると、開発者は と 発生する可能性があるケースを明示的に処理する必要があります。 strictNullChecks null undefined たとえば、次のコードを考えてみましょう。 const users = [ { name: 'Oby', age: 12 }, { name: 'Heera', age: 32 }, ]; const loggedInUser = users.find(u => u.name === 'Max'); // ^? { name: string; age: number; } console.log(loggedInUser.age); // ^ TypeError: Cannot read properties of undefined このコードはエラーなしでコンパイルされますが、「Max」という名前のユーザーがシステムに存在しない場合、実行時エラーがスローされる可能性があり、 は 返します。これを防ぐには、 コンパイラ オプションを有効にします。 users.find() undefined strictNullChecks TypeScript では、 によって返される または 可能性を明示的に処理する必要があります。 users.find() null undefined const loggedInUser = users.find(u => u.name === 'Max'); // ^? { name: string; age: number; } | undefined if (loggedInUser) { console.log(loggedInUser.age); } と の可能性を明示的に処理することで、実行時エラーを回避し、コードをより堅牢でエラーのないものにすることができます。 null undefiined 厳密な関数の型 { "compilerOptions": { "strictFunctionTypes": true } } を有効にすると、TypeScript のコンパイラがよりインテリジェントになります。バージョン 2.6 より前の TypeScript では、関数の引数の チェックされませんでした。これにより、関数が間違った型の引数で呼び出された場合、実行時エラーが発生します。 strictFunctionTypes 反変性が たとえば、関数型が文字列と数値の両方を処理できる場合でも、文字列のみを処理できる関数をその型に割り当てることができます。その関数に数値を渡すことはできますが、実行時エラーが発生します。 function greet(x: string) { console.log("Hello, " + x.toLowerCase()); } type StringOrNumberFn = (y: string | number) => void; // Incorrect Assignment const func: StringOrNumberFn = greet; // TypeError: x.toLowerCase is not a function func(10); 幸いなことに、 オプションを有効にするとこの動作が修正され、コンパイラはコンパイル時にこれらのエラーを検出して、関数の型の非互換性に関する詳細なメッセージを表示できるようになります。 strictFunctionTypes const func: StringOrNumberFn = greet; // ^ Type '(x: string) => void' is not assignable to type 'StringOrNumberFn'. // Types of parameters 'x' and 'y' are incompatible. // Type 'string | number' is not assignable to type 'string'. // Type 'number' is not assignable to type 'string'. クラスプロパティの初期化 { "compilerOptions": { "strictPropertyInitialization": true } } 最後に重要なことですが、 オプションを使用すると、値として 含まない型の必須クラス プロパティの初期化をチェックできます。 strictPropertyInitialization undefined たとえば、次のコードでは、開発者は プロパティを初期化するのを忘れています。デフォルトでは、TypeScript はこのエラーを検出しないため、実行時に問題が発生する可能性があります。 email class UserAccount { name: string; email: string; constructor(name: string) { this.name = name; // Forgot to assign a value to this.email } } ただし、 オプションが有効になっている場合、TypeScript はこの問題を強調表示します。 strictPropertyInitialization email: string; // ^ Error: Property 'email' has no initializer and // is not definitely assigned in the constructor. 安全なインデックス署名 { "compilerOptions": { "noUncheckedIndexedAccess": true } } オプションは モードの一部ではありませんが、プロジェクトのコード品質の向上に役立つ別のオプションです。これにより、インデックス アクセス式の戻り値の型が または であるかどうかをチェックできるようになり、実行時エラーを防ぐことができます。 noUncheckedIndexedAccess strict null undefined キャッシュされた値を保存するためのオブジェクトがある次の例を考えてみましょう。次に、キーの 1 つの値を取得します。もちろん、目的のキーの値が実際にキャッシュに存在するという保証はありません。 デフォルトでは、TypeScript は値が存在し、その型が であると想定します。これにより、実行時エラーが発生する可能性があります。 string const cache: Record<string, string> = {}; const value = cache['key']; // ^? string console.log(value.toUpperCase()); // ^ TypeError: Cannot read properties of undefined TypeScript で オプションを有効にするには、 戻り値の型についてインデックス アクセス式をチェックする必要があります。これにより、実行時エラーを回避できます。これは、配列内の要素へのアクセスにも当てはまります。 noUncheckedIndexedAccess undefined const cache: Record<string, string> = {}; const value = cache['key']; // ^? string | undefined if (value) { console.log(value.toUpperCase()); } 推奨される構成 説明したオプションに基づいて、タイプ セーフを最適化するために、プロジェクトの ファイルで オプションと オプションを有効にすることを強くお勧めします。 tsconfig.json strict noUncheckedIndexedAccess { "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true, } } オプションをすでに有効にしている場合は、 オプションの重複を避けるために、次のオプションを削除することを検討してください。 strict strict: true noImplicitAny useUnknownInCatchVariables strictBindCallApply noImplicitThis strictFunctionTypes strictNullChecks strictPropertyInitialization また、型システムを弱めたり、実行時エラーを引き起こす可能性がある次のオプションを削除することをお勧めします。 keyofStringsOnly noStrictGenericChecks suppressImplicitAnyIndexErrors suppressExcessPropertyErrors これらのオプションを慎重に検討して構成することで、TypeScript プロジェクトで最適なタイプ セーフとより良い開発者エクスペリエンスを実現できます。 結論 TypeScript は、コンパイラと型システムを絶えず改良しながら、長い進化を遂げてきました。ただし、下位互換性を維持するために、TypeScript の構成はより複雑になり、型チェックの品質に大きな影響を与える可能性のある多くのオプションが追加されました。 これらのオプションを慎重に検討して構成することで、TypeScript プロジェクトで最適なタイプ セーフとより良い開発者エクスペリエンスを実現できます。どのオプションを有効にし、プロジェクト構成から削除するかを理解しておくことが重要です。 特定のオプションを無効にした場合の結果を理解すると、それぞれのオプションについて情報に基づいた決定を下せるようになります。 厳密に型指定すると結果が生じる可能性があることに留意することが重要です。 JavaScript の動的な性質に効果的に対処するには、単に変数の後に「数値」または「文字列」を指定するだけでなく、TypeScript についてよく理解する必要があります。 発生する型関連の問題をより効果的に解決するには、より複雑な構造と、ライブラリとツールの TypeScript ファーストのエコシステムに精通する必要があります。 そのため、コードを書くのにもう少し労力が必要になるかもしれませんが、私の経験に基づくと、長期的なプロジェクトではこの労力は価値があります。 この記事から何か新しいことを学んでいただければ幸いです。これはシリーズの最初の部分です。次の記事では、TypeScript の標準ライブラリの型を改善することで、より優れた型安全性とコード品質を実現する方法について説明します。ご期待ください。読んでいただきありがとうございます! 役立つリンク TSConfig リファレンス いかなるタイプ ヌルおよび未定義