Почти то же самое я могу сделать с интерфейсами и псевдонимами типов.
Например
Классы могут реализовывать либо интерфейсы, либо псевдонимы типов.
interface Shape {
area(): number;
}
type Perimeter = {
perimeter(): number;
}
class Rectangle implements Shape, Perimeter {
}
Их можно комбинировать для создания новых интерфейсов/псевдонимов типов.
interface A {
a: string;
}
type B = {
b: string
}
interface C extends B {
c: string;
}
type D = A & {
d: string;
}
Есть ли семантическая разница между интерфейсами и аннотациями типов?
@KarolMajewski Да, и, к сожалению, лучший ответ не является принятым ответом.
@Paleo: этот пост очень хорош, я думаю, но он говорит только о незначительных технических различиях между интерфейсами и псевдонимами типов. Он вообще не говорит о семантических различиях






Интерфейсы могут расширять другие интерфейсы, а также могут реализовывать классы. Интерфейсы также могут использовать слияние объявлений:
type A = {
a: string;
}
type A = { // <-- error: duplicate identifier A
b: string;
}
interface A {
a: string;
}
interface A { // <-- okay, A is { a: string, b: string }
b: string;
}
edit: изменено расширение на *implement
редактировать 2: пересечения не совпадают с расширением. Рассмотрим следующее:
interface A {
num: number;
str: string;
}
type B = A & { // <-- this is okay, but overwrites the num property type to number & string
arr: any[];
num: string;
}
interface C extends A { // <-- error: C incorrectly extends A
num: string;
}
редактировать 3: еще одно потенциально существенное отличие для некоторых людей (хотя и не обязательно семантическое различие) заключается в том, что типы перечислены в подсказках (по крайней мере, в vscode), а интерфейсы - нет.
type также может расширять class: type MyType = MyClass & { otherProp: string; }. Кажется, слияние объявлений - единственная разница?
Еще одно отличие: type может быть составлен из | и может быть псевдонимом примитивных типов.
Мне они кажутся незначительными техническими различиями, а не семантическими различиями, которые могут помочь мне выбрать правильный инструмент для ситуации.
@Paleo, который не расширяется, это тип пересечения. Хотя я хотел сказать, что интерфейсы могут использовать классы воплощать в жизнь, что я исправил в своем посте.
@ user1283776 да, различия незначительны, но самая большая семантическая разница заключается в том, что интерфейсы могут реализовывать классы, расширять другие интерфейсы и сливаться друг с другом. Существует правило tslint под названием interface-over-type-literal, и люди из palantir объясняют это так: «Интерфейсы обычно предпочтительнее литералов типов, потому что интерфейсы могут быть реализованы, расширены и объединены».
@user1283776 user1283776 Когда интерфейс расширяет другой интерфейс, реальной семантической связи родитель/потомок не существует. Это так же, как с первым интерфейсом & новых членов.
см. мое редактирование, есть различия в пересечениях и реализациях, которые могут повлиять на безопасность типов в вашем приложении.
@RobbieMilejczak Но вы можете сделать: interface C extends A { num: string & number; }, и вы получите точно такой же результат, как и при пересечении типов. (EDIT: нет, пересечение типов не "перезаписывает тип свойства num". Это все еще пересечение.)
то, что вы можете использовать синтаксис для получения одного и того же результата, не делает его таким же, а string & number — это ужасный результат, которого вы никогда не хотели бы. Интерфейсы обеспечивают защиту от этого, а пересечения типов — нет.
Я не уверен, что вы думаете, что вы говорите, но это не имеет большого смысла. Вы не хотите иметь тип с num: string & number, это непригодно. Если вы используете типы и пересечения, вы можете получить этот тип, не зная об этом. Ошибка, которую обеспечивает интерфейс, желательна, и поведение пересечений и расширения интерфейса явно несопоставимо. Пересечение и расширение — это не одно и то же, и если вы используете их взаимозаменяемо, вы получите странные типы, такие как number & string.
@RobbieMilejczak: Можете ли вы объяснить семантическую разницу между интерфейсами и типами не только технически? Как будто кто-то может объяснить разницу между классами и объектами, поскольку классы являются шаблонами для объектов, а объекты являются экземплярами классов. Я не понимаю последствий «самой большой семантической разницы в том, что интерфейсы могут реализовывать классы, расширять другие интерфейсы и сливаться друг с другом» для 90+% моих вариантов использования. Я все еще ищу любые возможные семантические указания о том, следует ли использовать интерфейсы или псевдонимы типов в этих случаях.
В 90% случаев использования вы можете использовать оба варианта, псевдонимы типов имеют несколько ошибок при работе с пересечениями и объединениями, а также при попытке расширить их или как пример в моем посте, но в остальном они почти идентичны по поведению для большинства случаи применения. Я бы просто выбрал тот, который вы предпочитаете стилистически
Существуют технические различия между interface и type: здесь хорошо описано.
Однако для случаев, когда можно использовать и type, и interface, семантической разницы нет вообще.
interface и пересечении type в TypeScriptВ TypeScript иерархия между интерфейсами — это просто способ определения интерфейсов. Но как только они определены, между интерфейсами не возникает реальных отношений родитель-потомок. Например:
interface Named {
name: string
}
interface Person extends Named {
age: number
}
interface Animal {
name: string
age: number
}
Здесь Person и Animal одного типа. Как только они определены, они будут обрабатываться компилятором точно так же, когда их использует другой код:
function useNamed(named: Named) {
}
let p: Person = /* ... */
let a: Animal = /* ... */
useNamed(p) // OK
useNamed(a) // also OK, because if 'Animal' is compatible with
// `Named` then it is a `Named`
Вот почему тот же тип можно создать и с помощью тип перекрестка:
type Engine = Named & {
age: number
}
Из спецификации:
Intersection types represent values that simultaneously have multiple types. A value of an intersection type A & B is a value that is both of type A and type B. (source: TypeScript Specification)
Наш тип Engine — это и Named, и дополнительное определение: семантически это то же самое, что и наследование интерфейса. И тип Engine здесь точно такой же, как Person и Animal.
Спасибо! Если я могу задать сопутствующие дополнительные вопросы: как вы думаете, нужны ли интерфейсы и типы в TypeScript, или вы думаете, что только один из них будет включен, если TypeScript будет переработан с нуля?
@ user1283776 Я не уверен. Но IMO текущий синтаксис в порядке. Есть несколько способов сделать одно и то же, это похоже на JavaScript. Мы можем сказать, что интерфейсы — это «типы в фигурных скобках»: тогда синтаксис type someType = { … } — это анонимный интерфейс, присвоенный типу, точно так же, как функция — именованная константа, но const myFn = function () { … } — это анонимная функция, назначенная константе.
Возможный дубликат Typescript: интерфейсы против типов