error TS7017: Element implicitly has an 'any' type because type 'XXXXX' has no index signature.を解決する

コンパイラオプション`--noImplicitAny`が有効化されていないTypeScriptだと次のコードはコンパイルエラーにならない。

```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`型を明示する。

```typescript
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」を使う。


```typescript

// 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;
}
```

次のように想定外の引数は、コンパイルエラーとして弾ける。

```typescript
// コンパイルが通る
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`の返り値型を明示したい。

```typescript
// `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,
    };
}
```

こんな感じ。

```typescript
function loadFactors(): { [G in Grade]: number } {
    return {
        'Bronze': 1.0,
        'Silver': 1.5,
        'Gold': 2.0,
    };
}
```

もしくはこう。


```typescript
type GradeObject<T> = { [G in Grade]: T }

function loadFactors(): GradeObject<number> {
    return {
        'Bronze': 1.0,
        'Silver': 1.5,
        'Gold': 2.0,
    };
}
```