Typescript: Интерфейсы против типов

В чем разница между этими утверждениями (интерфейс против типа)?

interface X {
    a: number
    b: string
}

type X = {
    a: number
    b: string
};

Ответ 1

Согласно спецификации языка TypeScript:

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

Спецификация продолжает упоминать:

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

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

может быть написано как псевдоним типа

type Point = {
    x: number;
    y: number;
};

Однако это означает, что следующие возможности будут потеряны:

  • Интерфейс может быть назван в предложении extends или реализовать, но псевдоним типа для литерала типа объекта больше не может иметь значение true, начиная с TS 2.7.
  • Интерфейс может иметь несколько объединенных объявлений, но псевдоним типа для литерала типа объекта не может.

Ответ 2

Обновление 2019 года


Текущие ответы и официальная документация устарели. А для новичков в TypeScript используемая терминология непонятна без примеров. Ниже приведен список актуальных различий.

1. Объекты/Функции

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

интерфейс
interface Point {
  x: number;
  y: number;
}

interface SetPoint {
  (x: number, y: number): void;
}

Введите псевдоним

type Point = {
  x: number;
  y: number;
};

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

2. Другие типы

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

// primitive
type Name = string;

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];

3. Продлить

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

Интерфейс расширяет интерфейс

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

Псевдоним типа расширяет псевдоним типа

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

Интерфейс расширяет псевдоним типа

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }

Псевдоним типа расширяет интерфейс

interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };

4. Реализует

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

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

class SomePoint implements Point {
  x: 1;
  y: 2;
}

type Point2 = {
  x: number;
  y: number;
};

class SomePoint2 implements Point2 {
  x: 1;
  y: 2;
}

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

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

5. Декларация о слиянии

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

// These two declarations become:
// interface Point { x: number; y: number; }
interface Point { x: number; }
interface Point { y: number; }

const point: Point = { x: 1, y: 2 };

Ответ 3

Начиная с TypeScript 3.2 (ноябрь 2018 г.), верно следующее:

enter image description here

Ответ 4

https://www.typescriptlang.org/docs/handbook/advanced-types.html

Одно отличие состоит в том, что интерфейсы создают новое имя, которое используется повсюду. Типа aliases не создают нового имени - например, сообщения об ошибках не будут использовать псевдоним.

Ответ 5

Примеры с типами:

//создаем древовидную структуру для объекта. Вы не можете сделать то же самое с интерфейсом из-за отсутствия пересечения (&)

type Tree<T> = T & { parent: Tree<T> };

//тип, чтобы ограничить переменную, чтобы назначить только несколько значений. Интерфейсы не имеют объединения (|)

type Choise = "A" | "B" | "C";

//благодаря типам вы можете объявить тип NonNullable благодаря условному механизму.

type NonNullable<T> = T extends null | undefined ? never : T;

Примеры с интерфейсом:

//вы можете использовать интерфейс для ООП и использовать "инструменты" для определения каркаса объекта/класса

interface IUser {
    user: string;
    password: string;
    login: (user: string, password: string) => boolean;
}

class User implements IUser {
    user = "user1"
    password = "password1"

    login(user: string, password: string) {
        return (user == user && password == password)
    }
}

//вы можете расширить интерфейсы с другими интерфейсами

    interface IMyObject {
        label: string,
    }

    interface IMyObjectWithSize extends IMyObject{
        size?: number
    }

Ответ 6

документация объяснила

  • Одно отличие состоит в том, что интерфейсы создают новое имя, которое используется везде. Псевдонимы типов не создают новое имя - например, сообщения об ошибках не будут использовать псевдоним name. В более старых версиях TypeScript псевдонимы типов не могли быть расширены или реализованы (и при этом они не могли расширять/реализовывать другие типы). Начиная с версии 2.7, псевдонимы типов можно расширять, создавая новый тип пересечения
  • С другой стороны, если вы не можете выразить какую-либо форму с помощью интерфейса, и вам необходимо использовать тип объединения или кортежа, обычно рекомендуется использовать псевдонимы типов.

Интерфейсы против псевдонимов типа