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

Ответы:

576

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

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

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

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

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

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

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

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

  • Интерфейс может быть назван в предложении extends или реализовать, но псевдоним типа для литерала типа объекта больше не может иметь значение true, начиная с TS 2.7.
  • Интерфейс может иметь несколько объединенных объявлений , но псевдоним типа для литерала типа объекта не может.
Бинарное дерево березы
источник
109
Что означает «несколько объединенных объявлений» во втором разнице?
Джрахали
66
@jrahhali, если вы определяете интерфейс дважды, машинопись объединяет их в один.
Андрей Федоров
39
@jrahhali, если вы определили тип дважды, машинопись выдает ошибку
Андрей Федоров
18
@jrahhaliinterface Point { x: number; } interface Point { y: number; }
Науэль Греко
20
Я считаю, что первый пункт extends or implementsуже не тот. Тип может быть расширен и реализован с помощью class. Вот пример typescriptlang.org/play/…
dark_ruby
781

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 };
jabacchetta
источник
9
Если официальная документация устарела, где можно подтвердить предоставленную вами информацию?
iX3
60
Исходя из этого поста, кажется, что единственная причина выбора интерфейса вместо псевдонима типа заключается в том, что вы хотите использовать функцию объединения объявлений (пункт 5) интерфейсов. Кроме того, они эквивалентны (и я бы сказал, что псевдонимы типов предлагают более краткий синтаксис).
Maxedison
17
Я всегда использую интерфейсы для литерала типа объекта, в противном случае использование типов имеет больше смысла, и я думаю, что объединение объявлений не должно использоваться в любом случае, на самом деле я никогда не ожидаю, что интерфейс будет объявлен в другом файле проекта с некоторыми дополнительные свойства, проверка типов сделана изначально, чтобы сделать вашу жизнь проще, а не усложнить с помощью этих ниндзя-подобных интерфейсов: D
Ахмед Камаль
8
Так что, по сути, это «почти личный» выбор того, что нам действительно удобно? Помимо одной причины, вы можете просто использовать typeили interface? Я все еще смущен, когда я должен использовать один или другой.
Джозеф Бриггс
7
Может ли кто-нибудь предоставить какую-нибудь мотивацию, почему вы хотите объединить интерфейс? Это кажется потенциально смущающим для меня. Почему вы хотите распространить определение вашего интерфейса на разные блоки?
Vanquish46
95

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

введите описание изображения здесь

Кароль Маевски
источник
9
Не могли бы вы предоставить больше информации о том, как была сгенерирована предоставленная вами таблица / изображение? например, исходный код или ссылки на документацию
iX3
23
да, я имел в виду источник контента, а не его презентацию.
iX3
3
Я не верю, что класс может расширять тип или интерфейс, и я не могу понять, почему вы хотели бы это сделать ??
Дэн Кинг
7
Старайтесь не размещать изображения текста, вместо этого включайте фактический текст прямо в ваш пост. Изображения текста не могут быть легко проанализированы или доступны для поиска, а также недоступны для слабовидящих пользователей.
Эндрю Маршалл
2
Эта таблица не имеет никаких источников для поддержки ее содержимого, и я бы не стал полагаться на нее. Например, вы можете определить рекурсивные типы, используя typeопределенные ограничения (а в TypeScript 3.7 эти ограничения также исчезли). Интерфейсы могут расширять типы. Классы могут реализовывать типы. Кроме того, представление данных в виде скриншота таблицы делает ее полностью недоступной для людей с нарушениями зрения.
Михал Мищин
22

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

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

nickf
источник
30
Теперь это устарело и больше не соответствует истине начиная с TypeScript 2.1. Видеть. medium.com/@martin_hotell/...
demisx
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
    }
Przemek Struciński
источник
1

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

  1. Невозможно расширить тип объединения, используя интерфейс
  2. Не удается расширить общий интерфейс
dmudro
источник
-2

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

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

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

Лю Лэй
источник