68
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypeScript interfaceの拡張あれこれ

Last updated at Posted at 2018-10-16

TypeScriptのinterface拡張に関するちょっとしたメモ書きです。

基本

以下のような定義のinterfaceがあったとします。(例えばライブラリなどで宣言されていたり)

型定義.d.ts
interface iFoo {
  bar: string
  baz: number|string
}

この定義は自分(のアプリケーションコード内)で勝手にプロパティを追加宣言して拡張することができます。

my-app.ts
// iFooに新しくquxプロパティを定義
interface iFoo {
  qux: boolean
}

// 3種のプロパティが揃ってないとエラーになる
const foo: iFoo = {
  bar: "bar",
  baz: 0,
  qux: true,
}

これは Declaration Merging と言うそう。

しかし制限もあり、元々あるプロパティの型を上書きすることはできません。

// barプロパティはstring型として定義済みなのでNG
interface iFoo {
  bar: number // Error!
}

ただしextendすれば条件付きで変えることは可能です。
例えばbazプロパティ(number|string型)を変更する場合、

  • any型のように元の型にも適合するような緩い型にする
  • number型のように受け取れる型を狭める

形での拡張はできます。

// number|string => any: OK
interface iFooExtend extends iFoo {
  baz: any
}

// number|string => number:これもOK
interface iFooExtend extends iFoo {
  baz: number
}

しかし以下のように拡張しようとすると怒られます。

interface iFooExtend extends iFoo {
  baz: number|string|boolean // Error!
}

これはnumber|string型にはboolean型が適合しない(代入できない)ためです。
要は元の型に代入できるような型であることが条件になっています。

どうしても違法拡張したいとき

いったん定義を弱める拡張を行ってから、再度拡張するのはOKらしいです。

interface iFooWeaken extends iFoo {
  baz: any
}

interface iFooExtend extends iFooWeaken {
  baz: number|string|boolean
}

さらにWeakenという型を作って経由することで記述を簡略化することができます。(以下コードはこちらから引用)

type Weaken<T, K extends keyof T> = {
  [P in keyof T]: P extends K ? any : T[P];
};

interface iFooExtend extends Weaken<iFoo, 'baz'> {
  baz: number|string|boolean;
}

やや邪道感が漂いますが、interface拡張を頻繁に行いたいときは便利そうです。

参考

68
32
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
68
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?