LoginSignup
101

More than 1 year has passed since last update.

TypeScript: 異なる2つの型システム「公称型」と「構造的部分型」

Last updated at Posted at 2020-02-11

本稿では、異なる2つの型システム「公称型」と「構造的部分型」の差異について説明します。

型の互換性とは?

まず型の互換性とは何でしょうか? TypeScriptでは、ある型に別の型を代入できるかどうかが「互換性」の判断基準になります。例えば、1numberに代入できるので、1numberに互換しています。一方、truenumberに代入できないので、truenumberとは互換していないことになります。

異なる2つの型システム

TypeScriptは、互換性の有無の判断基準(つまり、代入できるかどうかの基準)が、JavaやPHPなどとは異なっています。どのように異なるのか、それぞれ見ていきましょう。

公称型 - 継承関係に着目するJava

JavaやPHPは、公称型(nominal typing)という型システムを採用しています。公称型では、型の互換性は、オブジェクト同士の継承関係(is-a関係)に着目して判断します。

例えば、AnimalUserの2つのクラスが、全く同じnameプロパティを持っていて、同じように取り扱えたとしても、お互いに継承関係が無い場合は、User型の変数にAnimalインスタンスを代入することも、その逆もできません。互換性がないわけです。逆に、CatクラスがAnimalを継承している場合、Animal型の変数にCatは代入可能です。CatAnimalとの互換性があることになります。

Astah_-__no_title_____.png

AnimalUserが同じプロパティ、同じメソッドを持っていれば、オブジェクトの「能力」は同じです。なので、Userを使うところにAnimalを使えても良さそうです。それが仕様上の間違いだとしても、論理エラーにはならないためです。

しかし、こうした交換を許さないのが公称型なのです。あくまで、継承関係があるかが大事。「能力」よりも「血統」重視の型システムと言えそうです。

構造的部分型 - 構造に着目するTypeScript

TypeScriptは、構造的部分型(structural subtyping)という型システムを採用しています。構造的部分型は、継承関係ではなく、オブジェクトが持っているプロパティが互換しているかどうか、つまり構造が同じかどうかに着目します。

例えば、AnimalUserの2つのクラスに、継承関係がなかったとしても、同じnameプロパティを持っていれば、User型の変数にAnimalを代入できます。その逆も可能です。つまり、お互いに互換性があるわけです。

Astah_-__no_title_____.png

class Animal {
  public name: string = ''
}

class User {
  public name: string = ''
}

let user: User = new User()
let animal: Animal = new Animal()
user = animal // OK
animal = user // OK

AnimalUserが同じインターフェイスならば、オブジェクトの「能力」は同じです。継承関係がなくとも、「能力」が同じかどうかが大事。「血統」よりも「能力」重視なのが構造的部分型と言えそうです。

・・・

本稿では、インターフェイスが同じならTypeScriptでは互換性があると説明しました。しかし、これは正確ではありません。同じインターフェイスを持ったクラス同士でも、互換性がない、つまり、代入できない場合があります。それについては次の投稿で説明していきたいと思います。


最後までお読みくださりありがとうございました。Twitterでは、Qiitaに書かない技術ネタなどもツイートしているので、よかったらフォローお願いします:relieved:Twitter@suin

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
101