TypeScript 2.1のkeyofとかMapped typesがアツい

## About me

@Quramy

* フロントエンドエンジニア的なことをして生きています。
* TypeScript + AngularでSPA作ってます。

---

## 2.1 RC is coming!

2016.11.08現在、2.1 RCがリリースされた。
[TypeScript blog](https://blogs.msdn.microsoft.com/typescript/2016/11/08/typescript-2-1-rc-better-inference-async-functions-and-more/)によると、

* async/awaitのDown transpileがES5/ES3でも利用可能に(~2.0ではES2015以上をtargetにしないと使えなかった)
* 型推論がより賢くなった

と書いてある。

2.1.1にどのような変更が入ったかは、 [@vvakame先生の記事](http://qiita.com/vvakame/items/305749d3d6dc6bf877c6) に詳しく記載されてる


---

## 2.1.4

[2.1のRoadmap](https://github.com/Microsoft/TypeScript/wiki/Roadmap#21-november-2016) を読むと、

* Static types for dynamically named properties
* Mapped types

というのが載っている。今日の本題はこいつら。

---

## Static types for dynamically named properties
[https://github.com/Microsoft/TypeScript/pull/11929](https://github.com/Microsoft/TypeScript/pull/11929)

feature 名長い。。。

そして「動的に命名されたプロパティのための静的型」とか、最早何のこっちゃ感。

このfeatureでは、`keyof`という新しいキーワードと、`T[K]` という2つの記法が追加された。

---

### `keyof`

`keyof T` で「type Tのプロパティ名の直和型」を表現するtypeが記述できる

```ts
interface User {
  name: string;
  age: number;
}

type UserKey = keyof User;
```

と書くと、 `UserKey` は `'name' | 'age'` というtypeになるよ、ということ。

---

### `T[K]`

`T[K]` で「`T` に対してK型でアクセスして得られるtype」を表現できる

`K` は、`keyof T` を満たすtypeであれば良いので、例えば

```ts
type UserName = User['name'];
```

とすると、`UserName` のtypeは `string` になる。 `User['age']` とすれば `number`。

---

### 利用例

```ts
interface Observable<T> {
    pluck<K extends keyof T>(key: K): Observable<T[K]>;
}

let user$: Observable<User>;
let name$ = user$.pluck('name'); // Observable<string>
```

以前は `name$ = user$.pluck<string>('name')` のように、動的にアクセスした先の型情報を自分で補う必要があった。

---

## Mapped types
[https://github.com/Microsoft/TypeScript/pull/12114](https://github.com/Microsoft/TypeScript/pull/12114)

言ってしまえば**(半)動的にtypeを生成する機能**。

```ts
{[P in K]: T}
```

という記法が可能に。`K` は stringにアサイン可能なtypeであればよく、 `K` に対する値は `T` というtypeになるよ、と言う意味。

```ts
type Item = { a: string, b: number, c: boolean };

type T1 = { [P in "x" | "y"]: number };  // { x: number, y: number }
type T2 = { [P in "x" | "y"]: P };  // { x: "x", y: "y" }
type T3 = { [P in "a" | "b"]: Item[P] };  // { a: string, b: number }
type T4 = { [P in keyof Item]: Date };  // { a: Date, b: Date, c: Date }
type T5 = { [P in keyof Item]: Item[P] };  // { a: string, b: number, c: boolean }
type T6 = { readonly [P in keyof Item]: Item[P] };  // { readonly a: string, readonly b: number, readonly c: boolean }
type T7 = { [P in keyof Item]: Array<Item[P]> };  // { a: string[], b: number[], c: boolean[] }
```

---

Mapped typesにより、既存typeのkey情報を再利用しつつ、新しいtypeを定義することができる:

```ts
type Freeze<T> = {
    readonly [P in keyof T]: T[P];
};

let frozenUser: Freeze<User>;
frozenUser.age = 18; // error TS2540: Cannot assign to 'age' because it is a constant or a read-only property.
```


---

### 利用例1

オブジェクトの各key & valueに対して処理を行う関数の定義：

```ts
declare function mapObject<K extends string, T, U>(
  obj: {[P in K]: T},
  fn: (x: T, k?: K) => U
): {[P in K]: U};

const nameLengths= mapObject({
    firstName: 'Yosuke',
    lastName: 'Kurami'
}, (x => x.length));

// type of nameLengths: { firstName: number, lastName: number};
```

なお、`type Record<K extends string, T> = {[P in K]: T}` というtypeがlib.es6.d.tsに定義済み.

---

### 利用例2

Boxingのサンプル([Angular2のReactive FormGroup](https://angular.io/docs/ts/latest/api/forms/index/FormGroup-class.html)を題材にしました)

```ts
interface FormControl<S> {
    setValue(value: S): void;
    reset(): void;
    valueChanges: Observable<S>;
    /* etc... */
}

type FormControls<T, K extends keyof T> = {[P in K]: FormControl<T[P]>};

interface FormGroup<T, K extends keyof T> {
    controls: FormControls<T, K>;
    set(v: Partial<T>): void;
};

declare var formBuilder: {
    group<T, K extends keyof T>(obj: T): FormGroup<T, K>;
}

const user: User = {
    name: 'Quramy',
    age: 32,
};

const formGroup = formBuilder.group(user);
controls.name.valueChanges.subscribe(x => {/** ... */});
```

`name` をboxingした FormControl(controls.name)は、`name` の型に従って、`Observable<string>` が返ってくる

---

## まとめ

* TypeScript 2.1.4では、動的にプロパティを扱う関数の型定義が書きやすくなった
* コレクションやオブジェクトを操作する関数・ライブラリをより賢く記述できるようになった
* Immutable.jsやRx、lodash 等がより改善される...かも？


## おまけ

TypeScript 2.1ではLanguage Service関連の機能追加(quickfix, implementation jump等)も含まれるので、
エディタプラグイン屋さんにも嬉しい！


---

*2016.12.08追記*

2.1.4(2.1系安定版) のリリース案内 https://blogs.msdn.microsoft.com/typescript/2016/12/07/announcing-typescript-2-1/ では、keyofやMapped Typesにも触れられています。