2016.12.12 追記
コメントにある通り、この挙動は意図したものではないようです。(Nightly 2.2.0 dev.20161211で挙動も変わったようです)
残念ですが、今のところPartial TypeからStrict Typeを得る一般的な方法はないようです。
もともとの動機は、下のような2つの型を別々に書きたくないという話だったのですが、
type PersonPartial = { name: string, age?: number, gender?: "male"|"female" };
type PersonStrict = { name: string, age: number, gender: "male"|"female" };
とりあえず、
type PersonRequired = { name: string };
type PersonOptional = { age: number, gender: "male"|"female" };
type PersonPartial = PersonRequired & Partial<PersonOptional>;
type PersonStrict = PersonRequired & PersonOptional;
とでもするしかなさそうです。
TypeScript 2.1がリリースされました。
やはり keyof
/ Mapped type の注目度が高いようで、qiitaにもネタが集まりつつありますね。いいことです。
TypeScript 2.1のkeyofとかMapped typesがアツい
TypeScript 2.1 で setState() が型安全になるぞー
やりたいこと
さて、以下のような2つの型があるとします。1
type PersonStrict = { name: string, age: number };
type PersonPartial = { name?: string, age?: number };
Partial<T>
を使えば、 PersonPartial
は以下のように PersonStrict
から得ることができるというのは、上に挙げた「TypeScript 2.1 で setState() が型安全になるぞー」でも紹介されていました。
type PersonPartial = Partial<PersonStrict>;
では、逆に PersonPartial
から PersonStrict
を得ることはできるでしょうか? 2
type Strict<T> = ???; // これをどう定義すれば、下のように PersonStrict を得られるか?
type PersonStrict = Strict<PersonPartial>;
こたえ
素直に考えると、これ(↓)でできそうな気がします。が、実際にやってみると、これでは各メンバは省略可能なままです。
type Strict<T> = { [K in keyof T]: T[K] };
const person: Strict<PersonPartial> = { name: "iwata" }; // 代入できてしまう
ですが、これに ()
をおまじないとして付加すると、見事に strict typeに変わりました。
type Strict<T> = { [K in (keyof T)]: T[K] };
const person: Strict<PersonPartial> = { name: "iwata" }; // age がないというエラーになる
TypeScript面白いですね。
なぜそうなる?
正直に言うと、分かりません。
想像でしかないですが、一つ目の例の [K in keyof T]
というのはそれ自体が一つのイディオムで、この場合に限り省略可能かどうかをMapped typeに引き継ぐための処理がなされているのではないかなあと。
これに()をつけて、keyof T
を先に評価させることで、「Tのプロパティ名」ではなく「単なる "name" と "age" の直和型」に意味が変わるので、省略可能かどうかという情報が欠落するということかなあと考えています。
ご存じの方がいたら教えてください。
免責事項
ドキュメント上にこの挙動に関する記述を見つけることはできなかったので、実はこれは仕様ではなくて不具合だったみたいな話になるかもしれません。
その場合はごめんなさい。