Интерфейс или тип в typescript? Что использовать?
Вам нужно знать различие между типом и интерфейсом, чтобы уметь правильно выбрать и использовать.
Книга — Эффективный TypeScript: 62 способа улучшить код.
ПРАВИЛО 13. Знайте разницу между type и interface
Если вы хотите определить именованный тип в TypeScript, то у вас есть два варианта:
(Также можно использовать class, но он относится к понятиям среды выполнения JavaScript и тоже вводит значение.
Что же вам следует использовать, type или interface ? Граница между этими вариантами с течением времени размылась настолько, что в некоторых ситуациях можно использовать и то и другое. Обратите внимание на оставшиеся различия между ними и следуйте постоянству применения того или другого в конкретных ситуациях. Еще вам нужно знать, как прописать один и тот же тип при помощи любого из этих способов. Это поможет легче воспринимать код TypeScript, использующий их.
Сперва рассмотрим сходства: типы State , указанные выше, являются практически неотличимыми друг от друга. Если вы определите значение IState либо TState с дополнительным свойством, то ошибки в результате будут идентичными:
Вы можете использовать сигнатуру индекса и для interface , и для type :
type TDict = < Typescript интерфейс или тип: string >; interface IDict
Также можно определить типы функций в обоих случаях:
type TFn = (x: number) => string; interface IFn < (x: number): string; >const toStrT: TFn = x => '' + x; // ok const toStrI: IFn = x => '' + x; // ok
Псевдоним типа выглядит более естественно для этого простейшего типа функции, но если тип будет иметь еще и свойства, тогда декларация будет выглядеть иначе:
type TFnWithProperties = < (x: number): number; prop: string; >interface IFnWithProperties
Этот синтаксис легче запомнить, зная, что в JavaScript функции являются вызываемыми объектами.И псевдонимы типов, и интерфейсы могут быть обобщенными:
type TPair = < first: T; second: T; >interface IPair
interface может расширять type (с некоторыми оговорками, упомянутыми ниже) и наоборот:
interface IStateWithPop extends TState < population: number; >type TStateWithPop = IState & < population: number; >;
Снова типы оказались идентичными. Оговорка заключается в том, что interface не может расширять сложные типы вроде типов объединений. Если вам нужно именно это, придется использовать type и & . Класс может реализовывать как interface , так и простой тип:
class StateT implements TState < name: string = ''; capital: string = ''; >class StateI implements IState
Так в чем же отличия? Одно вы уже видели: существуют типы объединения, но не существует интерфейсов объединения:
Расширение типов объединений может быть весьма полезно. Если у вас есть раздельные типы для переменных Input и Output и отображения из имени в переменную:
type Input = < /* . */ >; type Output = < /* . */ >; interface VariableMap
тогда может понадобиться тип, приписывающий переменной имя. Например:
type NamedVariable = (Input | Output) & < name: string >;
Этот тип не может быть выражен через interface . В целом Type имеет больше возможностей, чем interface . Он может выступать в качестве объединения, а также пользоваться более продвинутыми возможностями вроде отображения или условных типов.Помимо этого, он может более ясно выражать кортежи и типы массивов:
type Pair = [number, number]; type StringList = string[]; type NamedNums = [string, . number[]];
Вы можете выразить что-либо подобное кортежу, используя interface:
interface Tuple < 0: number; 1: number; length: 2;>const t: Tuple = [10, 20]; // ok
Но это неудобно и отбрасывает методы кортежа, такие как concat . Лучше использовать type .Тем не менее у interface есть некоторые возможности, отсутствующие у type . Одна из них — это то, что interface может быть дополнен. Возвращаясь к примеру со State , вы могли бы добавить поле population другим способом:
interface IState < name: string; capital: string; >interface IState < population: number; >const wyoming: IState = < name: 'Wyoming', capital: 'Cheyenne', population: 500_000 >; // ok
Это называется объединением деклараций. Вы наверняка встречали его ранее. Главным образом оно используется с файлами деклараций типов. Для его поддержки уместно использовать interface , поскольку в декларациях типов могут быть пропуски, которые должны заполнять пользователи. TypeScript использует слияние, чтобы получать разные типы для разных версий стандартной библиотеки JavaScript. Интерфейс Array , к примеру, определен в lib.es5.d.ts . По умолчанию — это все, что вы получаете. Однако если вы добавите ES2015 к записи lib вашего tsconfig.json , то TypeScript также включит lib.es2015.d.ts . Это добавляет (посредством слияния) еще один интерфейс Array с дополнительными методами вроде find , характерными для ES2015 .
В итоге вы получаете общий тип Array , богатый методами. Слияние поддерживается в обычном коде так же, как декларации. Если же вам важно, чтобы никто не мог в дальнейшем дополнять тип, то используйте type . Вернемся к вопросу выбора между type и interface . Для сложных типов выбора у вас нет: необходимо использовать псевдоним типа. Но что насчет более простых типов объектов, которые могут быть представлены и другим путем? Чтобы ответить на этот вопрос, вам следует учитывать согласованность и возможное дополнение. Если вы работаете с кодом, который постоянно использует interface , то придерживайтесь interface . Если же в нем применяется type , используйте type .В проектах, не имеющих установленного стиля, вам следует поразмыслить над возможностью дополнения. Если вы опубликуете декларации типов для API , пользователям будет удобно вставлять новые поля через interface при изменении API . Однако для типа, который используется внутри проекта, слияние деклараций может оказаться ошибкой, поэтому отдайте предпочтение type .
Книга — Профессиональный TypeScript. Разработка масштабируемых JavaScript-приложений
Cтр 119. Глава — Интерфейсы
Также хорошо описывает различие между типом и интерфейсом и что где использовать.
Книга — TypeScript быстро
Cтр. 56 Глава — Базовые и пользовательские типы
Описывает различие между классом, типом и интерфейсом.
In Typescript, what is the difference between type and interface?
I mostly use type for external data, for example from a JSON file, or if you are only writing functions without using OOP classes.
The accepted answer is out-of-date. Updated explanation posted here (since that thread seems to be favored by Google): stackoverflow.com/questions/37233735/…
6 Answers 6
Interfaces can be extended
interface A < x: number; >interface B extends A
interface C < m: boolean; >// . later . interface C
Type aliases, however, can represent some things interfaces can’t
type NumOrStr = number | string; type NeatAndCool = Neat & Cool; type JustSomeOtherName = SomeType;
So in general if you just have a plain object type, as shown in your question, an interface is usually a better approach. If you find yourself wanting to write something that can’t be written as an interface, or want to just give something a different name, a type alias is better.
Type aliases, however, can represent some things interfaces can’t It seems to me your examples NeatAndCool and JustSomeOtherName can be created as interface that extend the existing Neat , Cool or SomeType types.
Indeed the second and third examples can be created with an interface: interface NeatAndCool extends Neat, Cool <> interface JustSomeOtherName extends SomeType <>
Extending two interfaces will not always produce the same result as an intersection type, and not all types can be extended (whereas all types can be aliased or put into unions/intersections)
Putting things in perspective
let myAge = 25; let totalStatesInMyCountry = 25
Look both the variables are equal, that is (myAge === totalStatesInMyCountry) but their context is totally different.
This is similar case for typescript’s types and interfaces . Look for Foo
Foo as a type and as an interface looks same, similar to (myAge === totalStatesInMyCountry) , but this is just special case. Their context is totally different.
When to think about interface instead of types ?
- You are enforcing that this class must have these methods. You are thinking about interfaces not types.
- You want to create multiple implementation of same contract. Like for contract toHumanReadableNumber(number) different implementations needed are 100000 -> 100,000 , 100000 -> 100K and 100000 -> 100 Thousands . Lets say you are solving this problem by creating different classes for each requirement. You are thinking about classes and interfaces and not classes and types
- Interfaces are there to make system loosely coupled like implementing SOLID principles.
- Interfaces are substitute to multiple inheritances. A class can only inherit from single class, but can have multiple interfaces.
- For a class Point < x:number,y:number, distanceFromCenter():number>, in context of interfaces, only distanceFromCenter():number matters.
Old Answer (before 31st Dec 2022)
Differences between these too are already in this thread.
Here type Foo and interface Foo looks almost similar so its confusing.
interface is contract that the following properties (here foo:string ) should be there in a object. interface is not class . It is used when language does not support Multiple Inheritance. So interface can be a common structure between different classes.
class Bar implements Foo < foo: string; >let p: Foo = < foo: 'a string' >;
But type and interface are used in very different context.
let foo: Foo; let today: Date = new Date();
Here type of foo is Foo and today is Date . Its like a variable decleration which holds the information of typeof other variable. type is like a superset of interfaces, classes, function signature, other types or even values (like type mood = ‘Good’ | ‘Bad’ ). At the end type describes the possible structure or value of a variable.