（typescript）object の インデクサーに String 型の値が使えないエラーの対処

(20190722) タイトルを、objectのインデクサーに関するエラーだったため意味的に間違っていたので下記の通りタイトルを変更いたしました。

- 変更前：「（typescript）object の インデックスに String 型の値が使えないエラーの対処」
- 変更後：「（typescript）object の インデクサーに String 型の値が使えないエラーの対処」

## 問題：ObjectのインデクサーとしてString型の変数を使用できない。
関数リストのようなObjectから、Stringでメソッド名を渡して動的に関数を呼び出したいと思い、下記の処理を書いたところ、TypescriptのErrorとなりました。

```Demo.ts
const dateFormatFunctionList = {
  Y: function(date:Date):String{ return date.getFullYear().toString()},
  y: function(date:Date):String{ return (date.getFullYear().toString()).slice(2) },
}

function getFormattedDate(date:Date, format:String):String{
    //javascriptの動的なメソッド呼び出し
    return dateFormatFunctionList[format](date);
}

getFormattedDate(new Date(), 'Y'); //2019
getFormattedDate(new Date(), 'y'); //19

```
```error.txt
型 'String' はインデックスの型として使用できません。ts(2538)
```
![Demo.ts.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342307/bfc7884a-254a-afd3-923a-08f57057b643.png)


## 原因：typescript では、ObjectのインデクサーにStringは使用できないため。
typescript はオブジェクトのインデクサーとして、String を使用すると上記のようなエラーとなるようでした。
（javascript 標準ではブラケット表記法と呼ばれる方法で鍵括弧（[]）に文字列を渡す方法でアクセスできるようなのです。しかし、本来文字列で動的にオブジェクトにアクセスする場合は連想配列（Map）を使うことを想定するようです。）
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Property_Accessors

## 解決方１：小文字string型をインデクサーにしてエラーを回避する。
オブジェクトのインデクサーとして、プリミティブ型の`String`を渡すとエラーとなりますが、すべて小文字表記の`string`型をインデクサーとして渡しても、typescript がエラーにしないようです。
https://fedidat.com/550-typescript-string-index/
そこで、Stringで渡している値でtoString()を行うことで、プリミティブでない小文字`string`型に変換されエラーが出なくなりました。（併せて`dateFormatFunctionList` をany型に一度変換することで、const の自動型推論で付いた型を解除しました。）

```Demo.ts
  function getFormattedDate(date:Date, format:String):String{
      //javascriptの動的なメソッド呼び出し
      return (dateFormatFunctionList as any)[(format.toString())](date);
  }

```

これでエラーがなくなりました。


## 解決法2：「string 型の任意のkeyを持つオブジェクト」というInterfaceを自作する。
この問題は「デフォルトのオブジェクト型に対してインデクサーにStringが使えない」というのが、そもそもの問題なので、「string 型の任意のkeyを持つオブジェクト」のinterfaceを自作してキャストすることで解決できました。

https://stackoverflow.com/questions/34727936/typescript-bracket-notation-property-access/55108590#55108590?newreg=759affa3989c472192ee81cc30f84bef

下記の通りIndexableInterface を作成します。



```IndexableInterface.ts
export default interface IndexableInterface {
  [
   key: string
  ]: any;
}
```

このinterface をアクセス対象のオブジェクトで指定します。

```Demo.ts
import IndexableInterface from './IndexableInterface'
const dateFormatFunctionList = {
    Y: function(date:Date):String{ return date.getFullYear().toString()},
    y: function(date:Date):String{ return (date.getFullYear().toString()).slice(2) },
}
  
  function getFormattedDate(date:Date, format:string):String{
      //javascriptの動的なメソッド呼び出し
      return (dateFormatFunctionList as IndexableInterface)[format](date);
  }
  
  getFormattedDate(new Date(), 'Y'); //2019
  getFormattedDate(new Date(), 'y'); //19
```
これで、自動でString型のformatを小文字string型にキャストしてくれ、先程のエラーが出なくなりました。


## その他: IndexableInterfaceのkeyにプリミティブString型を割り当てられない
IndexableInterfaceのkeyに対して、プリミティブの方のString 型にキャストしようとすると、下記のエラーとなりできないようでした。
![Demo.ts2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342307/5b5006e8-7dd1-d6cb-3a8e-237fc586ce90.png)

```error.txt
インデックス シグネチャのパラメーターの型は 'string' または 'number' でなければなりません。ts(1023)
```

つまり、

- typescriptでは、オブジェクトのインデクスのシグネチャの型 に使用できるのは、小文字string型、またはnumber型に限られる。

という制限があるようでした。

## 前提条件


```tsconfig.json
{
  "compilerOptions": {
    "outDir": "./built/",
    "sourceMap": true,
    "strict": true,
    "noImplicitReturns": true,
    "noImplicitAny": true,
    "module": "es2015",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": "node",
    "target": "es5",
    "lib": [
      "es2016",
      "dom"
    ]
  },
  "include": [
    "src/ts/**/*"
  ]
}
```

## 2019/07/31 追記 props の type に、小文字 string型を指定するとエラーが出る。
![Demo.ts3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342307/797cc182-e409-9fe3-6a26-56aea89f709e.png)

```
'string' only refers to a type, but is being used as a value here.Vetur(2693)
```

## string は typescript の型

vue の props に渡すのは 「props 名をkeyにしてその設定 object をvalueとする object」 という型のようで、key「type」の value として型を渡しているため、**string は単なる typescript の type** のため、設定できません。

## String は javascript の型オブジェクト
対して String は、グローバルな型オブジェクトなため、object の value に設定ができるため、上記のケースではエラーが出ないようです。この場合、propsは、**「String 型のデータとして変換できるデータ（文字列リテラル等）」**という型になり、型制限ができるようです。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String

こちらのサイトにも string と String の違いについて詳細に記載いただいており、参照させていただきました。
https://saku.io/difference-string-types-in-typescript/

つまり

- **string は type（ typescript の型で単なる type）**
- **String は グローバルな型オブジェクト（ javascript の型オブジェクト）**


ということのようです。

以上です。