LoginSignup
24
13

More than 3 years have passed since last update.

TypeScriptでnoImplicitAnyを導入するために、妥協してsuppressImplicitAnyIndexErrorsを導入する

Last updated at Posted at 2019-12-22

この記事はフラーアドベントカレンダー 2019 23日目の記事です。

noImplicitAnyとは?

TypeScriptには多くのCompiler Optionsが存在します。Compiler Optionsの設定によって、型に対しての書き方を厳密にすることも、緩くすることもできます。

型の厳密さに関するTypeScriptのCompiler Optionsの代表的なもの一つは--noImplicitAnyです。--noImplicitAnyの公式ドキュメントの説明は次の通りです。

Raise error on expressions and declarations with an implied any type.

実際のコードでみてみましょう。次のようなコードがあります。

function greeting(name) {
    return `Hello ${name}!`;
}

console.log(greeting("Ryota"));

このコードは、Compiler Optionsとして--noImplicitAnyを有効にしていた場合、次のようなエラーとなります。

TSError: ⨯ Unable to compile TypeScript:
main.ts:1:22 - error TS7006: Parameter 'name' implicitly has an 'any' type.

1 function loadMessage(name) {

1行目のgreeting関数のnameパラメーターが原因でコンパイルエラーになっています。

--noImplicitAnyは、「型を何も書かない場合は暗黙的にanyになるけど、それはだめで、何かしら型を明示的にしないとだめだよ。」というCompiler Optionsです。このコンパイルエラーを解決するために、次のように型を明示してあげましょう。

function greeting(name: string) {
    return `Hello ${name}!`;
}

console.log(greeting("Ryota"));

greeting関数は、stringをパラメーターとしてとることを明示的にしました。

ちなみに次のコードでも大丈夫です。

function greeting(name: any) {
    return `Hello ${name}!`;
}

console.log(greeting("Ryota"));

暗黙的なany型はだめですが、明示的にany型を記述することは全く問題ありません。

suppressImplicitAnyIndexErrorsとは?

Compiler Optionsとして--noImplicitAnyが有効になっている場合、次のコードはコンパイルエラーになります。

const gradeFactors = {
    gradeA: 1.0,
    gradeB: 0.5,
    gradeC: 0.3
};

const grade: string = 'gradeA';

const factor = gradeFactors[grade];

console.log(`${grade} : ${factor}`);

エラーは次の通りです。

main.ts:9:16 - error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ gradeA: number; gradeB: number; gradeC: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ gradeA: number; gradeB: number; gradeC: number; }'.

9 const factor = gradeFactors[grade];

stringは、{ gradeA: number; gradeB: number; gradeC: number; }のインデックスタイプとして使えないからです。インデックスアクセスの部分がコンパイルエラーとなります。

これを解決する方法は2つあります。

1つ目はアクセスできるように型を変える方法です。

gradeの型を次のようにstringから「gradeA | gradeB | gradeC」などにすることでコンパイルエラーを解消することができます。

const gradeFactors = {
    gradeA: 1.0,
    gradeB: 0.5,
    gradeC: 0.3
};

const grade: 'gradeA' | 'gradeB' | 'gradeC' = 'gradeA';
// これ以外にも以下などでもOK
// const grade = 'gradeA';
// const grade: 'gradeA' = 'gradeA';
// const grade: 'gradeA' | 'gradeB' = 'gradeA';

// または次のようにGrade型を作るのも有効
// type Grade = 'gradeA' | 'gradeB' | 'gradeC';
// const grade: Grade = 'gradeA';

const factor = gradeFactors[grade];

console.log(`${grade} : ${factor}`);

2つ目はコンパイルオプションを追加することです。

Compiler Options--suppressImplicitAnyIndexErrorsというオプションがあります。
これは「厳密な型チェックを一部妥協する」コンパイルオプションです。
--noImplicitAnyが有効な時に、先のコンパイルエラーのようなインデックスアクセスによるコンパイルエラーを抑制する機能があります。

先のコードは--noImplicitAnyに加えて、--suppressImplicitAnyIndexErrorsを有効にすることで、コンパイルエラーを解消することができます。

実プロダクトでnoImplicitAnyを導入するために妥協する

実プロダクトで--noImplictAnyを導入することを考えてみましょう。例えば、コードベースはある程度大きく、すでに数年開発が続き、複数人で開発していて、お金も生み出している実プロダクトで、です。そんなプロダクトを改善するために、今まで導入されていなかった--noImplictAnyを導入しましょう。

--noImplictAnyを導入するために、先に説明した「フィールドやメソッドのパラメータの型が明示的になっていない箇所」、「無効な型でインデクサにアクセスしている箇所」を修正する必要があります。

フィールドやメソッドのパラメータに対して、単純に型を明示する作業はある程度簡単です。(実際は変更箇所が多かったり、同僚とコンフリクトしないように気をつける必要があり、そんなに簡単ではありません。)

一方で、「無効な型でインデクサにアクセスしている箇所」を修正する作業はかなり大変になる場合があります。型を定義・変更しないといけない、その型の変更が伝版することがあるからです。

筆者個人の意見として、--noImplictAnyを導入するために、--suppressImplicitAnyIndexErrorsを使って妥協することは、有効な戦略だと思います。

24
13
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
24
13