Яка різниця між "заявити клас" та "інтерфейс" в TypeScript


116

У TypeScript при створенні .d.ts файлів декларації джерела, що є кращим і чому?

declare class Example {
    public Method(): void; 
}

або

interface Example {
    Method(): void;
}

Відмінності, які я можу сказати, полягають у тому, що інтерфейси не можуть мати статичних методів, тому для цього потрібно використовувати клас. Обидва не виробляють жодного результату JS, тож, можливо, це не має значення?



Я не відчуваю, що будь-який із цих дійсно допомагає описати, що ви б використовували один одного, оскільки вони обидва можуть виконати те саме, що не має JS.
Кріс

1
Коротко: За допомогою оголошення потрібно переконатися, що реалізація класу існує під час виконання, де з інтерфейсом не потрібно.
Хакім

Відповіді:


163

interfaceце для того, коли ви просто хочете описати форму предмета. Ніколи не існує генерації коду для інтерфейсів - вони виключно артефакт в системі типів. Ви не побачите різниці в генерації коду для класу залежно від того, чи має він implementsзастереження.

declare classпризначений для того, коли ви хочете описати існуючий клас (як правило, клас TypeScript, але не завжди), який буде присутній зовні (наприклад, у вас є два .ts файли, що компілюються у два файли .js, і обидва включаються через scriptтеги на веб-сторінці). Якщо ви успадковуєте за classдопомогою extends(незалежно від того, базовий тип був declare classчи звичайним class), компілятор збирається створити весь код для підключення прототипу ланцюга та конструкторів переадресації, а що ні.

Якщо ви спробуєте успадкувати той, declare classякий повинен був бути інтерфейсом, у вас виникне помилка виконання, оскільки цей сформований код буде посилатися на об'єкт без прояву виконання.

І навпаки, якщо ви просто implementінтерфейс, який повинен був бути declare class, вам доведеться самостійно повторно впроваджувати всі учасники, і ви не будете використовувати переваги повторного використання коду з потенційного базового класу та функцій яка перевіряє ланцюг прототипу під час виконання, відхилить ваш об'єкт як фактично не екземпляр базового класу.

Якщо ви дійсно нерозумні, якщо у вас є C ++, ви можете грубо мислити interfaceяк typedefі declare classяк externдекларація конструктора, який строго не має визначення у цьому блоці компіляції.

Від чистої боку споживання (написання імперативного коду, не додавання нових типів), єдина відмінність між interfaceі в declare classтому , що ви не можете newінтерфейс. Однак, якщо ви маєте намір extend/ implementодин із цих типів у новому class, ви абсолютно повинні правильно обрати між interfaceі declare class. Працює лише одна з них.

Два правила, які вам добре послужать:

  • Чи ім'я типу вирівнювання з функцією конструктора (з newякою можна викликати ), яка насправді присутня під час виконання (наприклад, Dateє, але JQueryStaticні)? Якщо ні , то ви точно хочетеinterface
  • Я маю справу зі складеним класом з іншого файлу TypeScript або чимось достатньо схожим? Якщо так , використовуйтеdeclare class

Насправді ви можете створити новий інтерфейс у typecript. Єдине обмеження - спадкування.
Олег Михайлик

3
Ви не можете викликати newоператора для типу інтерфейсу. Однак інтерфейси можуть мати побудову підписів, а це означає, що ви можете викликати newоператора на значення типу інтерфейсу. Це дуже відрізняється від того, як classпрацює, коли підпис конструкції знаходиться саме на назві типу, а не на виразі цього типу.
Райан Кавано

Якщо ви йдете шляхом додавання конструктора до інтерфейсу, він повинен бути ТІЛЬКИМ членом інтерфейсу, за винятком «статики» класу. Не поєднуйте інтерфейс функції конструктора з інтерфейсом побудованого об'єкта. У цьому випадку система типу дозволяє дуріти, як: new (new x ()), де x: інтерфейс.
Джеремі Белл

24

Ви можете реалізувати інтерфейс:

class MyClass implements Example {
    Method() {

    }
}

Тоді як declare classсинтаксис дійсно призначений для використання для додавання визначень типів для зовнішнього коду, який не записаний у TypeScript - тому реалізація "в іншому місці".


Отже, ви пропонуєте використовувати клас оголошень для опису коду, не записаного в TypeScript? Я б припустив, що це випадок, але у файлі jquery.d.ts, який зареєстрований, JQueryStatic є інтерфейсом, реалізованим за: декларувати var $: JQueryStatic, я мав би хоч це бути оголошеним класом $ {public static ...}
Кріс

Єдиною причиною, про яку я міг би придумати це, було б, якби ви не хотіли, щоб люди розширювали клас - за допомогою інтерфейсу знадобиться поставити всю реалізацію.
Фентон

Має сенс. Можливо, це було причиною.
Кріс

Добре тоді, тому я намагаюся вказати на декларації в іншій бібліотеці JS, тому я обов'язково вимагаю заявити. Код використовує статику в деяких бібліотечних функціях (класах) - поки що я ще не бачу статичного властивості або методу, вираженого у файлі декларації. О, і чи варто використовувати простір імен чи модуль?
jenson-button-event

13

Згідно з умовами мирян, declareвикористовується в .ts/ d.tsфайлах, щоб сказати компілятору, що слід очікувати, що ключове слово, яке ми declaringіснуємо в цьому середовищі, навіть якщо воно не визначене в цьому файлі. Це дозволить нам забезпечити безпеку типу при використанні оголошеного об'єкта, оскільки компілятор Typescript тепер знає, що інший компонент може надавати цю змінну.


6

Різниця між declareі interfaceв TS:

заявити:

declare class Example {
    public Method(): void; 
}

У наведеному вище коді declareповідомляється компілятору TS, що десь оголошено клас Example. Це не означає, що клас магічно включений. Ви як програміст несете відповідальність за доступність класу під час його декларування (з declareключовим словом).

інтерфейс:

interface Example {
    Method(): void;
}

Vir interface- це віртуальна конструкція, яка існує лише в typecript. Компілятор машинопису використовує його з єдиною метою перевірки типу. Коли код складений на javascript, вся ця конструкція буде викреслена. Компілятор машинопису використовує інтерфейси для того, щоб перевірити, чи мають об'єкти правильну структуру.

Наприклад, коли у нас є такий інтерфейс:

interface test {
  foo: number,
  bar: string,
}

Об'єкти, які ми визначаємо, мають цей тип інтерфейсу, повинні точно відповідати інтерфейсу:

// perfect match has all the properties with the right types, TS compiler will not complain.
  const obj1: test = {   
    foo: 5,
    bar: 'hey',
  }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.