TypeScriptの型パズルを紹介する

## そもそも型パズルとは?

<「型」を受け取り「型」を出力する >　関数の型バージョンのようなものを作る問題。
今回それをやってみたのですが、TypeScriptに関する基礎的な知識があっても
最初独特の慣れが必要だなと感じたので書かせていただきます。

### 何が最初難しいのか

基本的に type YOUR_ANSWER = nantara
としてYOUR_ANSWERという型を用意することになります。
すると...のところでは例えば以下のことができません。
- forループを回す
- オブジェクトなどのインスタンスを直接参照する

型関数を作るときの入力は、所詮「型」でしかありません。
type YOUR_ANSWER\<T\> = nantara
として右辺でジェネリクスに入れたTをこねくり回すわけなのですが、
例えばTがオブジェクトの時、型情報に含まれる「オブジェクトのキー」を参照したいときはどうすればいいのでしょうか。

### オブジェクトの型情報からキーを取り出すには

結論から書くと"in"と"keyof"を使います。

```typescript
type Dog = {
    name: string;
    run: () => void;
};

type PartialDog = {
	[P in keyof Dog]?: Dog[P];
}
```
例えばこういったコードがあったとしたら`P in keyof Dog`とすることで
Dogのキーが順々に取り出されます。
PartialDog は { name?: string, run?: () => void }
というオブジェクトの型になります。

https://qiita.com/ryo2132/items/ce9e13899e45dcfaff9b

### ではtupleのインデックスを取り出すには?
```typescript
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject<typeof tuple> 
```
このようにしたときにresultの持つ型情報が、
オブジェクト 
```typescript
{ tesla: 'tesla', 'model 3': 'model 3' //略 }
```
のようになるには、どうしたらいいのでしょうか。
いきなりだと難しいのでいくつかに分解します。
やることは、

- tupleの各要素にアクセスする
- それを順番にオブジェクトのキーにする
- プロパティも同じ

ちなみにさっきの手は使えません。

```typescript
type TupleToObject<T extends string[]> = {
  [P in keyof T] : T[P]
}
```
Tがさっきと違いtupleなので Pは順々にtupleのインデックスになります。

ちなみに自分はここで問題に触れました(製作者様とは何の関係もありません)

https://mosya.dev/type-challenges/challenge-tuple-to-object

## tupleが曖昧な人向け

### 概要
tuple型は[”型1”, “型2”, “型3”…]のように書きます。
特徴としては、一度定義されると
- 要素の数が変わらない
- 各要素の型が変わらない

ことです。

## 問題に戻る

### tupleの型情報を順に取得するには?

流れとしてはこのようになります。

- tupleの全ての要素をユニオンさせる
- それを"in"で順に取り出す

コードとしては以下になります。
```typescript
type TupleToObject<T extends string[]> = {
    [P in T[number]] : P
}
```

T[number]は 'tesla' | 'model 3' | 'model X' | 'model Y'
という型になります。「リテラル型」の「ユニオン型」です。

```typescript
{
    [P in T[number]] : P
}
```
とすることで { tesla: 'tesla', 'model 3': 'model 3' // 略}
というオブジェクトが作られます。
(teslaだけクォートで囲まれない。model 3は間にスペースがあるので囲まれているだけ)
また、 `P in T[number]`が`[]`で囲まれているのは、この記法の約束だと思ってください。

またこちらの記事も参考になります。

https://zenn.dev/luvmini511/articles/d89b3ad241e544

## 答え

```typescript
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type TupleToObject<T extends readonly string[]> = {
  [K in T[number]] : K 
}
type result = TupleToObject<typeof tuple> // { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
```

## tupleはreadonlyの配列か?

例えばさきほど、
```typescript
type TupleToObject<T extends string[]> =
```
のように書きましたが
```typescript
type TupleToObject<T extends readonly[]> =
```
もしtupleがreadonlyの配列ならこう書いてもいいのではないでしょうか。
ですがtupleは、
要素の合計数と　各「インデックス」に与えられた型
が変わらないだけです。インデックス1が、stringと指定されていたら、
それを新しい要素(同じく型はstring)に変更する事ができます。

ですが
```typescript
type TupleToObject<T extends readonly string[]> = 
```
としたらどうでしょうか。これは "変更不可で全ての要素がstringの配列" を受け取ります。

(結局配列だが、例のタプルに最も近い定義だと考えられる)

実際先ほど貼りましたmosya<TC>の回答では簡便のためか`any []`で制約されているのですが、
さらにその元ネタとなっているgithubの回答で最も評価を得ているのは`extends readonly string[]`でした。

https://github.com/type-challenges/type-challenges/issues/2737

## 補足 型定義にforループはない について

確かにforループは使えないのですが、Recursive Typesという概念があり、
型定義の中で「自分」を参照することで、リスト・ツリー・グラフなどのデータを表現できます。

### ツリー構造のコードを紹介
```typescript
type Tree<T> = {
  value: T;
  children: Tree<T>[];
};

const aTree: Tree<number> = {
  value: 1,
  children: [
    {
      value: 2,
      children: []
    },
    {
      value: 3,
      children: [
        {
          value: 4,
          children: []
        }
      ]
    }
  ]
};

```
具体的な例だとJSONに対しての定義に使われるみたいです。

https://blog.mitsuruog.info/2019/04/typesctipt-recursive-data-structure