0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypeScriptのコンパイルオプション一覧(2025年4月時点)

Posted at

TypeScriptのコンパイルオプションはどのように設定されているでしょうか?
大盛無料のノリで全部オンにしてもよいですが、何があるか把握してある程度は把握しておくことも大事です。

さらりと全て見ていきましょう。

※ 執筆時点のTypeScriptのバージョンは5.8です。
※ 間違っていたら指摘してください。

1. noImplicitAny

メソッドの引数の型を必ず指定させます。
下記のFAILの方のメソッドは引数 anything に型を指定していないため、コンパイルエラーとなります。

// Parameter 'anything' implicitly has an 'any' type.
function example1_FAIL (anything): void {
    console.log(anything);
}

// OK
function example1_SUCCESS (anything: unknown): void {
    console.log(anything);
}

2. strictNullChecks

型がnullセーフになります。
これがないとstringやnumberといった型にundefinedやnullを代入してもコンパイルエラーになりません。
つまり、下記のFAILのメソッドがまかり通ってしまうことになります。

// Type 'null' is not assignable to type 'number'.
const example2_FAIL: number = null;

// OK
const example2_SUCCESS: number|null = null;

3. strictFunctionTypes

共変(型を広くする)は許可するが、反変(型を狭くする)は許可しません。
例を見てみましょう。

// 反変(型を狭くする)
// Type '(value: number) => void' is not assignable to type '(value: string | number) => any'.
let example3: (value: number|string) => any;
example3 = function (value: number): void {
    console.log(value.toFixed());
};

// 上記の場合、以下が実行されるとランタイムエラーを発生させてしまう
example3('文字列');

// 共変(型を広くする)はOK
example3 = function (value: number|string|null|undefined): void {
    // value.toFixed() を実行したい場合はきちんと型を絞った上で実装しないとエラー
    console.log(value);
};

1つ目のメソッドでは、引数が number|string 型のメソッドに対して、引数が number 型のメソッドを代入しています。
この状態は危険であり、メソッドの内部では引数が number 型を前提とした処理が記載できてしまいます。一方で実際の引数の型は number|string 型 なので、 string 型を受け入れることが可能です。

例の通り number 型を前提とした処理が記載されていた場合、実行時エラーになってしまいます。
このことを防ぐのがstrictFunctionTypesコンパイルオプションになります。

逆に2つ目のメソッドは型を広くして代入しています。
これはエラーにはなりません。例えばこの中で value.toFixed() を実行したければ、型を絞ってから記載しないとコンパイルエラーとなるからです。

なので共変(型を広くする)は安全ですが、反変(型を狭くする)は危険なので許可しないという内容になります。

4. strictBindCallApply

bind call apply を使用した時に引数の型チェックを行うようになります。

function example4 (args: string): void {
    console.log(`${args.toUpperCase()}`);
}

// Argument of type 'number' is not assignable to parameter of type 'string'.
example4.bind(null, 100);
example4.call(null, 200);
example4.apply(null, 300);

そもそも bind apply call を使用したことはあるでしょうか?これらを使用すると this を使用する少し変わったメソッドの呼び出しが可能になります。
デフォルトだとこられの呼び出し時の引数が any になってしまうので、それらに適切に型を付けるオプションとなります。
表現を変えると、このオプションがOFFだと例のコードはコンパイルエラーになりません。(もちろん実行時エラーになります)

5. strictPropertyInitialization

クラス内で初期化されていないプロパティを禁止します。

// Property 'property1' has no initializer and is not definitely assigned in the constructor.
// Property 'property2' has no initializer and is not definitely assigned in the constructor.
// Property 'property3' has no initializer and is not definitely assigned in the constructor.
class example5_FAIL {
    property1: string;
    property2: string;
    property3: string;

    constructor () { }
}

// OK
class example5_SUCCESS {
    property1: string = 'OK';
    property2: string;
    property3: string|undefined;

    constructor () {
        this.property2 = 'OK';
    }
}

そもそもTypeScriptでクラスを使う機会自体が少ないかもしれませんが、覚えておいて損はないでしょう。
余談ですが、コンストラクタ内で別のメソッドを呼び出し、そのメソッド内で初期化をしていても検知できずにコンパイルエラーとなってしまった記憶があります。

少し不便に感じますが、コンストラクタ内からそのメソッドを呼び出して初期化するという暗黙的な依存が生まれるとも解釈できるので、仕方なさそうです。

6. strictBuiltinIteratorReturn

これは最近追加されたオプションで、イテレータをnext()で辿った時の型チェックを厳密にします。
どういうことでしょうか。

const example6: Iterator<string, BuiltinIteratorReturn> = ['one', 'two', 'three'].values();

// undefinedを踏んでしまうのでtoUpperCase()のところでランタイムエラーを起こしてしまう
// 'example6_FAIL.value' is possibly 'undefined'.
const example6_FAIL = example6.next();
console.log(example6_FAIL.value.toUpperCase());

// OK
const example6_SUCCESS = example6.next();
if (example6_SUCCESS.done) {
    // undefined 確定
    console.log(example6_SUCCESS.value);
} else {
    // string 確定
    console.log(example6_SUCCESS.value.toUpperCase());
}

example6string 型のIterableな値ですが、 next() でループをすると undefined を踏んでしまう可能性があります。
これは next() の仕様によるものです。

undefined を踏まずに安全にループをするには、 SUCCESSの方のメソッドのように example6_SUCCESS.done による分岐で参照が undefined でないか検証する必要があります。

7. noImplicitThis

関数内でthisを使う場合、明示的に型の指定を強制します。

const exmaple7 = {
    name: 'name',
    greetFail: exmaple7_FAIL,
    greetSuccess: exmaple7_SUCCESS
}

// 'this' implicitly has type 'any' because it does not have a type annotation.
function exmaple7_FAIL () {
    console.log(`${this.name}`);
}

type example7 = {
    name: string;
}

// 引数でthisの型を明示してあげればOK
function exmaple7_SUCCESS (this: example7): void {
    console.log(`${this.name}`);
}

FAILの方のメソッドの this.name は何を指しているのでしょうか?
exmaple7 を指しているのだと思われますが、これだとエラー以前に分かりにくいです。

SUCCESSの方のように、引数に型を明示した this を渡すことを強制させるオプションです。

8. useUnknownInCatchVariables

try catch での例外補足時、catch節に渡る値をunknown型とします。

// 'example8_FAIL' is of type 'unknown'.
try {
    throw 'ERROR';
} catch (example8_FAIL) {
    console.log(example8_FAIL.toUpperCase())
}

例では example8_FAILstring 型の前提で処理が書かれていますが、このオプションを有効にすると型エラーとなります。
string 型が期待されているのであれば、型を条件分岐で絞ったうえで処理を記載する必要があります。

余談ですが console.log() するだけなら unknown 型でも問題はありません。例えば console.log(example8_FAIL) とするのであれば何ら問題はありません。

9. alwaysStrict

グローバルにstrictモードを有効にします。
strictモードを有効にすると、JavaScriptに関する危険寄りのコーディングができなくなります。

// 例えば var let const なしでの暗黙的なグローバル変数の作成をエラーにする
// Cannot find name 'example9_FAIL'. Did you mean 'example1_FAIL'?
example9_FAIL = undefined;

// OK
const example9_SUCCESS = undefined;

JavaScriptでは const や let を宣言せず変数へ代入した場合、その変数はグローバル変数になるという特徴を持っています。
これでは初学者がうっかりconst等を宣言しないまま代入してしまい、知らずのうちにグローバル変数が生まれてしまう事故が発生しうります。
これらのようなJavaScriptが持っている「落とし穴」をコンパイルエラーにしてくれるのがこのオプションです。

10. noUnusedLocals

未使用のローカル変数をエラーにします。

function example10_FAIL (): void {
    // 'unused' is declared but its value is never read.
    const unused = 'unused';
    console.log('unused');
}

function example10_SUCCESS (): void {
    console.log('OK');
}

11. noUnusedParameters

未使用の引数があった場合エラーを吐きます。

// 'args2' is declared but its value is never read.
function example11_FAIL (args1: string, args2: string, args3: string): void {
    console.log(`${args1} ${args3}`);
}

// 使わないものは _ を先頭につけて表現すればOK
// ただ eslint ではエラー出ちゃうので注意
function example11_SUCCESS (args1: string, _args2: string, args3: string): void {
    console.log(`${args1} ${args3}`);
}

例文内にも記載していますが、引数名を _ から開始すればエラーにならないようになります。
ライブラリを使用しており、コールバック関数で2つ目の引数は使用したいが1つ目は使わない、という時に便利です。

12. exactOptionalPropertyTypes

オプショナルプロパティに対する、明示的な undefined の代入を禁止します。

type example12 = {
    property1: string;
    property2?: string;
}

// Type '{ property1: string; property2: undefined; }' is not assignable to type 'example12' with ...
const example12_FAIL: example12 = {
    property1: 'OK',
    property2: undefined
}

// OK
const example12_OK: example12 = {
    property1: 'OK'
}

明示的に代入したい場合は、オプショナルプロパティではなく undefined とのユニオン型にする必要があります。

13. noImplicitReturns

戻り値の型を厳密に評価します。

// Function lacks ending return statement and return type does not include 'undefined'.
function example13_FAIL (args1: number): string {
    if (args1 > 0) {
        return 'OK';
    }
}

// OK
function example13_SUCCESS (args1: number): string {
    if (args1 > 0) {
        return 'OK';
    }
    return '';
}

FAILの方の関数は条件によっては return する値がありません。
SUCCESSのように全ての分岐で string 型が返るようにする、あるいは戻り値の型を string|void とするべきです。

14. noFallthroughCasesInSwitch

switch文でbreak, returnが適切に使用されているか調べます。

function example14_FAIL (args1: number): void {
    switch (args1) {
        case 1: // Fallthrough case in switch.
            console.log(args1);
        case 2: // Fallthrough case in switch.
            console.log(args1);
        case 3: // Fallthrough case in switch.
            console.log(args1);
        default:
            console.log(args1);
    }
}

// OK
function example14_SUCCESS (args1: number): void {
    switch (args1) {
        case 1:
            console.log(args1);
            break;
        case 2:
            console.log(args1);
            break;
        case 3:
            console.log(args1);
            return;
        default:
            console.log(args1);
    }
}

case 毎に break や return を設けなさいということです。

15. noUncheckedIndexedAccess

配列アクセス時の型はundefinedとのユニオン型とします。

const example15: number[] = [1, 2, 3];

// Type 'number | undefined' is not assignable to type 'number'.
// Type 'undefined' is not assignable to type 'number'.
const example15_FAIL: number = example15[1];
// Object is possibly 'undefined'.
example15[1] += 1;

const example15_SUCCESS: number = example15[1] ?? 0;
example15[1] = example15[1] ?? 0 + 1;

JavaScriptでは存在しないインデックスを指定して配列にアクセスしても、エラーにはならず undefined を返します。
つまり配列へのアクセスは常に undefined を取得する可能性があるということです。

このオプションは賛否両論あるように思われます。配列へのアクセスをしている部分全てで undefined とのユニオン型となるので、厳密に扱いたい場合は undefined のチェックが至る所で発生することになります。

固定で要素を持っている配列があり、アクセスしても絶対に値があるのにもかかわらず undefined が付きまとい辟易することもあるでしょう。
儀式的な undefined チェックが至る所に発生し、コードの可読性も悪くなってしまいます。

個人的に思うのは、そういう場合でもコンパイルオプションをオフにするのではなく別の選択肢を考えてほしいと思っています。
例えば確実に値があると分かっているのであれば、その部分だけは Non-null assertion operator を使用すれば解決します。

const example15: number[] = [1, 2, 3];

const example15_FAIL: number = example15[1]!;

代入時に ! をつけてコンパイラに nullundefined の可能性がないことを教えています。

これを使うのだったらこのオプションを有効にする必要はないのでは?と考える方もいらっしゃるかもしれません。

アプローチは全て0か100ではないと思っています。つまり確実に値があることが自明である場所のみ ! を使用し、そうでない場所は undefined とのチェックを挟めばいいわけです。
コンパイラオプションの有効か無効かの2択ではなく、もっとたくさんの選択肢があってもいいのではないかと個人的に思っています。

16. noImplicitOverride

オーバライドの明示的な宣言を強制します。

class Example15_Parent {
    public example15_FAIL (): void {
        console.log('parent');
    }

    public example15_SUCCESS (): void {
        console.log('parent');
    }
}

class Example15_Child extends Example15_Parent {
    // This member must have an 'override' modifier because it overrides a member in the base class 'Example15_Parent'.
    public example15_FAIL (): void {
        console.log('child');
    }

    // OK
    public override example15_SUCCESS (): void {
        console.log('child');
    }
}

17. noPropertyAccessFromIndexSignature

オブジェクトのプロパティが存在するか不明瞭な場合、プロパティアクセスをインデックス記法に強制します。

type Example17 = {
    [key: string]: string;
  };

const example17: Example17 = {
    FAIL: 'NG',
    SUCCESS: 'OK',
}

// Property 'FAIL' comes from an index signature, so it must be accessed with ['FAIL'].
console.log(example17.FAIL);

console.log(example17['SUCCESS']);

インデックス記法とは [] を使ったアクセスのことです。
他にもドット . を使ってもプロパティにアクセスできますが、プロパティが存在するか不明瞭な場合はドット表記のアクセスを禁止するというものです。

18. allowUnusedLabels

使用されていないラベルにエラーを出します。
このオプションはfalseすることで有効になります。

example18_SUCCESS: for (let i = 0; i < 5; i++) {
    if (i === 1) {
        continue example18_SUCCESS;
    }
    if (i === 2) {
        // Unused label.
        example18_FAIL: true;
    }
    console.log(i);
}

そもそもJavaScriptでラベルが使われているのをあまり見たことがないです。

19. allowUnreachableCode

未到達コードにエラーを出します。
このオプションはfalseすることで有効になります。

function example19 (args1: number): number {
    if (args1 > 0) {
        return args1 + 1;
    } else {
        return args1 + 2;
    }
    // Unreachable code detected.
    return args1 + 3;
}

終わりに

後半はどちらかというとlint寄りのものが多かったと思います。
コンパイラオプションはこれからも追加される可能性があるので、日々チェックをしていけたらと思います。

これで安心してオールオンにできますね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?