В этом блоге в основном обсуждаются ключевые отличия interface vs type и рассказывается, как я буду использовать их в своей повседневной работе по разработке программного обеспечения.

Описать форму объекта или сигнатуру функции

Оба могут использоваться для описания формы объекта или сигнатуры функции.

Тем не менее, я в основном использую interface для описания объекта и использую type для описания сигнатуры функции, так как я точно знаю, что это определение функции из arrow function, которую невозможно реализовать в interface.

interface Point {
  x: number;
  y: number;
}

type SetPoint = (x: number, y: number) => void;

Другой сценарий, в котором я использую интерфейс, — это 「Подписи индексов」. Например, в динамически сгенерированном вводе мы не знаем заранее все имена свойств типа, но знаем форму значений.

interface Input {
  [index: string]: string | number;
}

Расширение и типы пересечения

На первый взгляд, расширение и пересечение — это похожие способы объединения типов. Однако принципиальная разница между ними заключается в том, как обрабатываются конфликты.

Для interface, если вы объявите свойство с тем же ключом, оно переопределит свойство в производном интерфейсе. Однако он должен быть совместим с предыдущим, иначе компилятор машинописного текста выдаст ошибку.

// This works pretty well, as it is compatible
interface Converter {
  convert: (value: number) => string;
}

interface StringNumberConverter extends Converter {
  convert: (value: number | string) => string;
}

// Oops...this will throw an error, as it is incompatible
interface Converter {
  convert: (value: number) => string;
}

interface StringNumberConverter extends Converter {
  convert: (value: string) => string;
}

Для типа свойства с одним и тем же ключом не обязательно должны быть совместимы. Вы можете объявить все, что угодно.

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

type StringNumberConverter = Converter & {
  convert: (value: string) => string;
}

Еще одно отличие, на которое следует обратить внимание, — это значение, которое они представляют. Когда я использую интерфейс, я хочу, чтобы он обозначал отношения「Родитель-потомок」. В противоположность этому, когда я использую шрифт, он обозначает только отношение 「Композиция」.

// 「Parent-Child」relationship. Cat derives from Animal.
interface Animal {
  name: string;
}

interface Cat extends Animal {
  color: string;
}

// 「Composition」relationship. Color and Size are two seperate species
type Color = {
  name: string
}
type Size = {
  size: 'small' | 'medium' | 'large'
}

type ColorSize = Color & Size

Орудия класса

Класс может реализовать интерфейс или псевдоним типа, но не может реализовать тип Union, так как в typescript класс может реализовать только тип объекта или пересечение типов объектов с статически известными членами.

Поэтому в повседневной разработке, если этот тип предназначен для реализации классом, я буду использовать interface вместо type.

type PartialPoint = { x: number; } | { y: number; };

// can not implement a union type
class SomePartialPoint implements PartialPoint {
  x = 1;
  y = 2;
}

Больше, чем типы объектов

В отличие от интерфейса, псевдоним типа также может использоваться для других типов. Часто используются такие, как тип объединения и кортежи.

type StringNumberPair = [string, number];
function doSomething(stringHash: [string, number]) {
  const [inputString, hash] = stringHash;
  console.log(inputString); // string
  console.log(hash);  // number        
}

Декларация слияния

interface открыто, а type alias замкнуто внутри себя. Интерфейс может быть определен несколько раз и будет рассматриваться как единый интерфейс с элементами всех объявлений, которые объединяются.

type Bear = {
  name: string;
}
// Error:Duplicate Identifier
type Bear = {
  age: number;
}

// works pretty well
interface Bear {
  name: string;
}

interface Bear {
  age: number;
}

const b: Bear = {
  name: '',
  age: 0
}

Работа с оператором типа typeof

В большинстве случаев, если вы хотите работать с оператором типа typeof, выбирайте type вместо interface.

function f() {
  return { x: 10, y: 3 };
}

type P = ReturnType<typeof f>;

/**
** type P = {
    x: number;
    y: number;
}
**/

Заключение

Когда в следующий раз вы запутаетесь, что использовать, попробуйте эту шпаргалку!