377
234

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-12-02
1 / 14

About me

@Quramy

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

2.1 RC is coming!

2016.11.08現在、2.1 RCがリリースされた。
TypeScript blogによると、

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

と書いてある。

2.1.1にどのような変更が入ったかは、 @vvakame先生の記事 に詳しく記載されてる


2.1.4

2.1のRoadmap を読むと、

  • Static types for dynamically named properties
  • Mapped types

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


Static types for dynamically named properties

https://github.com/Microsoft/TypeScript/pull/11929

feature 名長い。。。

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

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


keyof

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

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であれば良いので、例えば

type UserName = User['name'];

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


利用例

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

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

{[P in K]: T}

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

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を定義することができる:

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に対して処理を行う関数の定義:

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を題材にしました)

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にも触れられています。

377
234
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
377
234

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?