LoginSignup
67
32

More than 3 years have passed since last update.

TypeScript interfaceの拡張あれこれ

Last updated at Posted at 2018-10-16

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

v3.9.2で検証しています。(おそらく2.x, 3.x系いずれも動くと思われます)

基本

以下のような定義のinterfaceがあったとします。

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

この定義は自分で後からプロパティを追加して拡張することができます。

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

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

これは Declaration Merging と言うそう。

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

// barは定義済みなのでNG
interface iFoo {
  bar: number
}

ただしextendすれば条件付きで変えることは可能です。
any型のように引き続きnumber|string型に適合するような形に変えたり、number|string=>numberのように受け取れる型を狭める形での拡張はできます。

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

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

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

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

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

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

いったん定義を弱める拡張を行ってから、再度拡張するのは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拡張を頻繁に行いたいときは便利そうです。

参考

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