Help us understand the problem. What is going on with this article?

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

この記事はフラーアドベントカレンダー 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を使って妥協することは、有効な戦略だと思います。

RyotaMurohoshi
プログラミングが大好きで、 C#が大好きで、 .NETが大好きで、 LINQが大好きで、 JVM言語が大好きで、 ゲームで遊ぶことが大好きで、 ゲーム開発が大好きで、 頑張るのが大好きで、 Unityが大好きだったから...!
http://mrstar-memo.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした