コンパイラオプション--noImplicitAny
が有効化されていないTypeScriptだと次のコードはコンパイルエラーにならない。
// `strict`か`noImplicitAny`が有効だとコンパイルエラーになる
function calculatePoint(basePoint: number, grade: string): number {
const factor = {
'Bronze': 1.0,
'Silver': 1.5,
'Gold': 2.0,
};
return factor[grade] * basePoint;
}
--noImplicitAny
を有効にすると、このコードはコンパイルエラーになる。エラーログはこんな感じ。
error TS7017: Element implicitly has an 'any' type because type '{ 'Bronze': number; 'Silver': number; 'Gold': number; }' has no index signature.
このコンパイルエラーを解決する。
解決策1
手っ取り早く解決するには、factor
にany
型を明示する。
function calculatePoint(basePoint: number, grade: string): number {
const factor: any = {
'Bronze': 1.0,
'Silver': 1.5,
'Gold': 2.0,
};
return factor[grade] * basePoint;
}
ただこれは問題点を抱えている。
解決策2
「String Literal Types + Union Types + Type Aliases」を使う。
解決策1の問題点は引数grade
が、
Bronze
Silver
Gold
以外だった場合。
// 実行結果は、NaNに
console.log(calculatePoint(130, "Diamond"));
こうなる。
仕様としてcalculatePoint
は引数grade
は
Bronze
Silver
Gold
のみを引数としてとることを想定している。そこで、「String Literal Types + Union Types + Type Aliases」を使う。
// String Literal Types + Union Types + Type Aliases
type Grade = 'Bronze' | 'Silver' | 'Gold';
function calculatePoint(basePoint: number, grade: Grade): number {
// anyもいらない
const factor = {
'Bronze': 1.0,
'Silver': 1.5,
'Gold': 2.0,
};
return factor[grade] * basePoint;
}
次のように想定外の引数は、コンパイルエラーとして弾ける。
// コンパイルが通る
const grade: Grade = "Bronze";
console.log(calculatePoint(90, grade));
console.log(calculatePoint(100, "Gold"));
console.log(calculatePoint(120, "Silver"));
console.log(calculatePoint(130, "Bronze"));
// コンパイルエラー
// console.log(calculatePoint(130, "Diamond"));
// const anotherGrade: string = "Silver";
// console.log(calculatePoint(130, anotherGrade));
解決策3
error TS7017
の数が多すぎる場合、コンパイルオプションの--suppressImplicitAnyIndexErrors
を検討する。
例えば--noImplicitAny
は導入したいけれど、
error TS7017: Element implicitly has an 'any' type because type 'XXXXX' has no index signature.
が複数箇所で多数発生してしまい、--noImplicitAny
を入れるためには相当頑張らないといけない。
そんな場合、--noImplicitAny
と共に--suppressImplicitAnyIndexErrors
を導入すれば、次のコンパイルエラーを抑制できる。
error TS7017: Element implicitly has an 'any' type because type 'XXXXX' has no index signature.
これにより一部ではあるが、--noImplicitAny
の恩恵を受けることができる。
すでにお金を生んでいて、これからも開発を続けるプロジェクトにおいて、これの価値は大きい。
余談
こんな感じでメソッド化しているとする。loadFactors
の返り値型を明示したい。
// `strict`か`noImplicitAny`が有効だとコンパイルエラーになる
function calculatePoint(basePoint: number, grade: string): number {
const factor = loadFactors();
return factor[grade] * basePoint;
}
// `loadFactors`の返り値型を明示したい
function loadFactors() {
return {
'Bronze': 1.0,
'Silver': 1.5,
'Gold': 2.0,
};
}
こんな感じ。
function loadFactors(): { [G in Grade]: number } {
return {
'Bronze': 1.0,
'Silver': 1.5,
'Gold': 2.0,
};
}
もしくはこう。
type GradeObject<T> = { [G in Grade]: T }
function loadFactors(): GradeObject<number> {
return {
'Bronze': 1.0,
'Silver': 1.5,
'Gold': 2.0,
};
}