Интерфейс и класс в TypeScript
В С# существует довольно большая разница между интерфейсами и классами. Действительно, класс представляет ссылочный тип, так что мы можем фактически создавать объекты, смоделированные на этом классе, тогда как интерфейсы предназначены для контрактов, для которых знак класса используется для обеспечения существования определенного поведения. В частности, мы не можем создавать экземпляры интерфейсов.
Весь смысл использования интерфейсов - выявить поведение. Класс реализует его, предоставляя одну явную реализацию указанного поведения.
В этом случае, хотя интерфейсы могут содержать свойства, большую часть времени мы заботимся об интерфейсах из-за поведенческих проблем. Таким образом, большинство типов, интерфейсов - это просто контракты поведения.
На TypeScript, с другой стороны, я, кажется, что-то очень беспокоило меня, и, по правде говоря, я видел это не один раз, и это причина этого вопроса.
В одном учебнике я увидел это:
export interface User {
name: string; // required with minimum 5 chracters
address?: {
street?: string; // required
postcode?: string;
}
}
Но подождите минуту. Почему User
- это интерфейс? Если мы думаем, что С#, User
не должен быть интерфейсом. На самом деле, глядя на это, похоже, мы определяем тип данных User
вместо контракта поведения.
Думая, что мы делаем на С#, естественным было бы следующее:
export class User {
public name: string;
public address: Address;
}
export class Address {
public street: string;
public postcode: string;
}
Но эта функция использования интерфейсов, подобных нам с классами, просто определить тип данных, а не определять контракт поведения, кажется очень распространенной в TypeScript.
Итак, какие интерфейсы предназначены для TypeScript? Почему люди используют интерфейсы в TypeScript, как мы используем clases в С#? Как правильно использовать интерфейсы в TypeScript: устанавливать контракты поведения или определять свойства и объект должны иметь?
Ответы
Ответ 1
Учтите, что в Javascript данные часто обмениваются как простые объекты, часто через JSON:
let data = JSON.parse(someString);
Скажем, это data
представляет собой массив объектов User
, и мы передадим его функции:
data.forEach(user => foo(user))
foo
будет набираться следующим образом:
function foo(user: User) { ... }
Но подождите, ни в коем случае мы не делаем new User
! Должны ли мы? Должна ли нам писать класс User
и map
все data
для него, хотя результат будет точно таким же, Object
со свойствами? Нет, это было бы безумием только ради удовлетворения системы типов, но ничего не изменило бы о времени выполнения. Простой interface
, который описывает, как ожидается, что конкретный объект будет выглядеть ( "вести себя" ), вполне достаточен здесь.
Ответ 2
Я также пришел к Typescript с фона С# и задался вопросом о том же. Я думал о строках POCOs (это POTO вещь?)
Итак, какие интерфейсы предназначены для TypeScript?
В справочнике Typescript показано, что интерфейсы предназначены для " определения контрактов внутри вашего кода" .
Почему люди используют интерфейсы в Typescript, как мы используем классы в С#?
Я согласен с ответом @deceze здесь.
Джон Папа расширяется по предмету классов и интерфейсов в своем блоге . Он предлагает, что классы лучше всего подходят для "создания нескольких новых экземпляров, используя наследование, [и] одноэлементные объекты". Таким образом, исходя из намерения интерфейсов Typescript, как описано в Руководстве Typescript и мнениях одного человека, казалось бы, что классы не нужны для заключения контрактов в Typescript. Вместо этого вы должны использовать интерфейсы. (Ваши чувства С# все равно будут оскорблены.)
Интерфейсы должны быть правильно использованы в TypeScript: для установления контрактов поведения или для определения свойств и объекта должны иметь?
Если я понимаю вопрос, вы спрашиваете, должны ли интерфейсы устанавливать контракты поведение или контракты структуры. На это я бы ответил: оба. Typescript все еще можно использовать так же, как интерфейсы используются на С# или Java (например, для описания поведения класса), но они также позволяют описать структуру данных.
Кроме того, мой коллега получил меня за использование классов вместо интерфейсов, потому что интерфейсы не производят код в компиляторе.
Пример:
Этот TypeScript:
class Car implements ICar {
foo: string;
bar(): void {
}
}
interface ICar {
foo: string;
bar(): void;
}
создает этот Javascript:
var Car = (function () {
function Car() {
}
Car.prototype.bar = function () {
};
return Car;
}());
Попробуйте
Ответ 3
Интерфейсы в typescript аналогичны интерфейсам на С#, поскольку оба они предоставляют контракт. Однако в отличие от интерфейсов С#, которые содержат только методы typescript, интерфейсы могут также описывать поля или свойства, которые содержат объекты. Поэтому они также могут использоваться для вещей, которые напрямую не возможны с интерфейсами С#.
Основное различие между интерфейсами и классами в typescript заключается в том, что интерфейсы не имеют представления во время выполнения, и для них не будет никакого кода. Интерфейсы очень широко используются. Например, вы можете использовать объектные литералы для построения объектов, удовлетворяющих интерфейсу. Как:
let user: User = {
name: 'abc',
address: {
street: 'xyz',
},
};
Или вы можете назначить любые объекты данных (например, полученные через разбор JSON) на интерфейс (но ваши предварительные проверки должны утверждать, что это действительно достоверные данные). Поэтому интерфейсы очень гибкие для данных.
С другой стороны, классы имеют тип, связанный с ними во время выполнения, и генерируется код. Вы можете проверить тип во время выполнения с помощью instanceof
и установить цепочку прототипов. Если вы определяете User
как класс, он не будет действительным пользователем, если вы не вызываете функцию конструктора. И вы не можете просто определить любые подходящие данные как User
. Вам нужно будет создать новый экземпляр и скопировать свойства.
Мое личное эмпирическое правило:
- Если я имею дело с чистыми данными (из разных источников), я использую интерфейсы
- Если я моделирую что-то, у которого есть идентификатор и состояние (и, вероятно, связанные методы для изменения состояния), я использую класс.
Ответ 4
Как следует правильно использовать интерфейсы в TypeScript: для установления контрактов поведения или для определения свойств и объекта должны иметь?
Интерфейсы в TypeScript являются контрактами формы, описывающими ожидаемую структуру объекта. Если значение имеет определенную аннотацию интерфейса, вы ожидаете, что это объект с элементами, определенными в интерфейсе. Члены могут быть значениями или функциями (методами). Как правило, их поведение (тела функций) не является частью контракта. Но вы можете указать, являются ли они readonly
или нет.
Итак, какие интерфейсы предназначены для TypeScript? Почему люди используют интерфейсы в TypeScript, как мы используем clases в С#?
Typescript интерфейсы могут играть ту же роль, что и интерфейсы С#, если ожидается, что они будут реализованы классами TypeScript.
Но не только класс может реализовать интерфейс; любое значение может:
interface HelloPrinter {
printHello(): void
}
Следующий объект не является классом, но тем не менее реализует интерфейс:
{
printHello: () => console.log("hello")
}
Таким образом, мы можем сделать
const o: HelloPrinter = {
printHello: () => console.log("hello")
}
и компилятор TypeScript не будет жаловаться.
Объект реализует наш интерфейс, не заставляя нас писать класс.
Работа с интерфейсами более легкая, чем работа с классами (интерфейсами и).
Но если вам нужно знать имя типа (имя класса/интерфейса) во время выполнения, то классы являются правильным выбором, потому что имена интерфейсов известны только во время компиляции.
Ответ 5
Используя только собственный механизм десериализации, вы не можете десериализовать экземпляр определенного класса. Вы можете только десериализовать объект plain-old-javascript. Такие объекты могут придерживаться интерфейсов typescript, но не могут быть экземпляром класса. Если вам нужно иметь дело с данными, которые пересекают границу сериализации, например данные, ожидаемые от веб-службы, используйте интерфейсы. Если вам нужно самостоятельно генерировать новые экземпляры таких значений, просто создайте их буквально или создайте функцию удобства, которая возвращает их - объекты, которые придерживаются этого интерфейса.
Класс может сам реализовать интерфейс, но может быть запутанным, если вы планируете иметь дело с локально сконструированными экземплярами класса и десериализованными, восстановленными обычными объектами. Вы никогда не сможете опираться на классовую основу объекта, и поэтому не будет никакой пользы от его определения как класса для этой конкретной цели.
У меня был успех в создании модуля ServerProxy, ответственного за отправку кода из веб-сервиса - вызов webservice и возвращаемый результат. Если вы привязываетесь к моделям нокаутов или похожим, вы можете иметь класс, который инкапсулирует ui-связанную модель с конструктором, который знает, как поднять возвращаемый объект plain-old-javascript, который придерживается интерфейса webservice, экземпляр вашего модельного класса.