技術メモ

習得したIT技術の備忘録

【TypeScript】構造的部分型 (structural subtyping)を理解する

構造的部分型 (structural subtyping)とは

プログラミング言語の派生型(継承関係にある型)の方式の1つ。

派生型の方式は公称型(Nominal Typing)と構造的部分型(structural subtyping)があり、 TypeScriptは構造的部分型(structural subtyping)を採用している。

構造的部分型は、型の構造を比較し置換可能かを判定する。

つまりどういうことか?

  • Fruits型とApple型を定義する

  • それぞれ別の型なので、Fruits型の変数にApple型の変数を代入すると型エラーが発生しそうだけどしない

  • それははぜか?

    • Fruits型とApple型の構造を比較すると、両方string型のnameプロパティがある(構造が同じ)
    • 構造的部分型では「型の構造を比較し置換可能かを判定する」
    • Fruits型とApple型が構造が同じのためFruits型の変数にApple型の変数を代入してもエラーにならない
type Fruits = {
  name: string;
};

type Apple = {
  name: string;
};

const apple: Apple = {
  name: "ジョナゴールド",
};

// Fruits型の変数にApple型の変数を代入する
const fruits: Fruits = apple; // コンパイルエラーにならない

構造の比較について

コンパイルエラーになるパターン

以下のパターンはコンパイルエラーになる。

  • なぜエラーになるのか?
    • Apple型はcolorプロパティを持っているが、Fruits 型にはない
    • Apple型に必要なプロパティがない(=構造が一致していない)ためエラーになる
type Fruits = {
  name: string;
};

type Apple = {
  name: string;
  color: string;
};

const fruits: Fruits = {
  name: "ジョナゴールド",
};

// Apple型の変数にFruits型の変数を代入する
const apple: Apple = fruits // エラー「プロパティ 'color' は型 'Fruits' にありませんが、型 'Apple' では必須です。」

コンパイルエラーにならないパターン

以下のパターンはコンパイルエラーにならない。

  • なぜエラーにならないのか?
    • 構造の一致は必要なプロパティがあるかどうかで判定している
    • Fruits型に必要なstring型のnameプロパティは、Apple型にある
    • Apple型はFruits型に不要なcolorプロパティを持っているが、Fruits型は定義している以外のプロパティはあってもなくてもよい
    • Fruits型必要なプロパティがあるためFruits型とApple型は構造が一致していると判定され、Fruits型の変数にApple型の変数を代入できる
type Fruits = {
  name: string;
};

type Apple = {
  name: string;
  color: string;
};

const apple: Apple = {
  name: "ジョナゴールド",
  color: "赤",
};

// Fruits型の変数にApple型の変数を代入する
const fruits: Fruits = apple; // コンパイルエラーにならない

// 代入はエラーにならないがアクセスはエラーになる
fruits.color  // エラー「プロパティ 'color' は型 'Fruits' に存在しません。」

参考