1. ホーム
  2. typescript

TypeScriptのextendingとintersecting interfacesの違い?

2023-10-04 09:42:10

質問

以下のような型が定義されているとします。

interface Shape {
  color: string;
}

では、この型にさらにプロパティを追加する方法を以下に考えてみましょう。

拡張機能

interface Square extends Shape {
  sideLength: number;
}

交差点

type Square = Shape & {
  sideLength: number;
}

両方のアプローチの違いは何ですか?

また、完全を期すため、また好奇心からなのですが、同等の結果を得るための他の方法はあるのでしょうか?

どのように解決するのですか?

はい、あなたのシナリオに関連するかしないかの違いがあります。

おそらく最も重要なのは、両方のタイプに存在する場合、同じプロパティ キーを持つメンバーがどのように処理されるかの違いです。

考えてみてください。

interface NumberToStringConverter {
  convert: (value: number) => string;
}

interface BidirectionalStringNumberConverter extends NumberToStringConverter {
  convert: (value: string) => number;
}

extends は、派生インターフェースと同じキーを持つが互換性のないシグネチャを持つプロパティを宣言しているため、エラーとなります。

error TS2430: Interface 'BidirectionalStringNumberConverter' incorrectly extends interface 'NumberToStringConverter'.

  Types of property 'convert' are incompatible.
      Type '(value: string) => number' is not assignable to type '(value: number) => string'.
          Types of parameters 'value' and 'value' are incompatible.
              Type 'number' is not assignable to type 'string'.

しかし、交差型を採用すると

type NumberToStringConverter = {
  convert: (value: number) => string;
}

type BidirectionalStringNumberConverter = NumberToStringConverter & {
  convert: (value: string) => number;
}

何のエラーもなく、さらに与えられた

// And this is a good thing indeed as a value conforming to the type is easily conceived
const converter: BidirectionalStringNumberConverter = {
    convert: (value: string | number) => {
        return (typeof value === 'string' ? Number(value) : String(value)) as string & number; // type assertion is an unfortunately necessary hack.
    }
}

const s: string = converter.convert(0); // `convert`'s call signature comes from `NumberToStringConverter`

const n: number = converter.convert('a'); // `convert`'s call signature comes from `BidirectionalStringNumberConverter`

遊び場リンク

これはもう一つの興味深い違いにつながります。 interface の宣言はオープンエンドです。新しいメンバーはどこにでも追加することができ、複数の interface 宣言は、同じ宣言空間に同じ名前の宣言が複数あると マージされます。 .

マージ動作の一般的な使い方は次のとおりです。

lib.d.ts

interface Array<T> {
  // map, filter, etc.
}

array-flat-map-polyfill.ts

interface Array<T> {
  flatMap<R>(f: (x: T) => R[]): R[];
}

if (typeof Array.prototype.flatMap !== 'function') {
  Array.prototype.flatMap = function (f) { 
    // Implementation simplified for exposition. 
    return this.map(f).reduce((xs, ys) => [...xs, ...ys], []);
  }
}

このように extends 節が存在しないことに注意してください。別々のファイルで指定されているにもかかわらず、インターフェースは両方ともグローバルスコープにあり、両方のメンバーセットを持つ単一の論理インターフェース宣言に名前によってマージされます。(同じことが、少し異なる構文でモジュールスコープの宣言に対しても可能です)

対照的に、交差型は type 宣言に格納される交点型は閉じており、マージの対象にはなりません。

多くの相違点があります。TypeScriptハンドブックに、両方の構文について詳しく書かれています。また インターフェイス 高度なタイプ のセクションは特に関連性が高いです。