TypeScript の extends と and type の振る舞いの違い

📆November 25, 2020🔖 TypeScript

表題の通りなのだけれども、TypeScript の interfaceextends と、type& の振る舞いの違いが理解できていなかったので、いろいろ動かして整理してみた。

同名プロパティがあったときの振る舞いの整理をしたのが以下。

interface A {
  a: "A";
}

// 定義可能か? -> 🙅‍♂️ できない コンパイルエラー
interface B extends A {
  a: "B";
}

// 定義可能か? -> できるが、交差している部分がないので never 型になる
type C = A & { a: "B" };

interface D {
  a: string;
}

interface E extends D {
  a: "A";
}

// 定義可能か? -> できる
const e1: E = {
  a: "A",
};

// 定義可能か? -> 🙅‍♂️ できない コンパイルエラー
const e2: E = {
  a: "B",
};

// 定義可能か? -> 🙅‍♂️ できない コンパイルエラー
interface F1 extends D {
  a: number;
}

// 定義可能か? -> できるが never 型になる
type F2 = D & {
  a: number;
};

// 定義可能か? -> 🙅‍♂️ できない コンパイルエラー
interface G1 extends D {
  a: number | string;
}

// 定義可能か? -> できる。string かつ (number | string) なので、結局 string になる
type G2 = D & {
  a: number | string;
};

// 定義可能か? -> 🙅‍♂️ できない コンパイルエラー
const g: G2 = {
  a: 1,
};

extends

すでにプロパティが存在するものに対する上書きで、継承元の定義の集合をさらに広げることは不可能。コンパイルエラー。絞ることは可能。

type 交差型 &

すでにプロパティが存在するものに対する上書きで絞ることは同様に可能。広げることもできるが、ベン図の概念で、A かつ B の領域がない場合は never 型になる。


といいつつ、

同名のプロパティがある場合の挙動に関しては異なるのだけれども、まぎらわしいし、他の人も読みにくくなるので、Web アプリケーションのコードではあまり使わない方が良さそうかなと思った(ライブラリとかだと別だと思う)同名のプロパティがない場合には挙動は同じっぽいのでどちらを使うかはお好みで(僕はオブジェクト志向プログラミングの手癖で interface をよく使う)


こういうときに使ってるよとか、理解が間違ってるよ、みたいなのあったら Twitter で教えて下さい。