В чем разница между "Конструктором", "Статическим" и обычными интерфейсами в TypeScript? - PullRequest
0 голосов
/ 28 апреля 2019

TypeScript имеет два строковых интерфейса, называемых String и StringConstructor.

. Кроме того, спецификация языка TypeScript предоставляет этот пример кода, в главе 1, раздел 1.3:

interface JQuery {
  text(content: string);
}

interface JQueryStatic {
  get(url: string, callback: (data: string) => any);
  (query: string): JQuery;
}

В чем разница между JQuery / JQueryStatic и String / StringConstructor интерфейсами и связанными Static и Constructor интерфейсами?

РЕДАКТИРОВАТЬ: да, я знаю, что связанная книга 3 года устарела.

1 Ответ

1 голос
/ 29 апреля 2019

Я собираюсь перейти с String на RegExp, потому что есть складка с String Я упомяну позже *.


Интерфейс экземпляра (например, RegExp и JQuery) обычно представляет тип объекта, в котором может существовать несколько разных экземпляров. Связанный статический интерфейс (например, RegExpConstructor и JQueryStatic) обычно представляет тип объекта, который создает или возвращает этих экземпляров; и часто существует только один из этих статических объектов. Таким образом, существует только один RegExpConstructor объект, который может создавать множество RegExp объектов, и только один JQueryStatic объект, который может создавать множество JQuery объектов.

Одним из распространенных источников путаницы на практике является конфликт имен между значениями и типами . Имя одного статического объекта (например, RegExp или jQuery), как правило, совпадает с именем интерфейса экземпляра. Но тип этого статического объекта не является типом интерфейса экземпляра. Таким образом, значение с именем RegExp во время выполнения имеет тип RegExpConstructor, а не RegExp. И значение с именем jQuery во время выполнения имеет тип JQueryStatic, а не JQuery. Это сбивает с толку, но, вероятно, к лучшему, так как позволяет вам говорить во время выполнения, например, x instanceof Y и во время компиляции, что тип x равен Y.

В любом случае, если есть свойство или метод, поведение которого зависит от конкретного экземпляра, оно обычно находится на интерфейсе экземпляра. Если есть какое-либо свойство или метод, поведение которого не зависит от конкретного экземпляра, это обычно находится на статическом интерфейсе.


Интерфейс конструктора - это статический интерфейс, который позволяет вам использовать оператор new для создания новых экземпляров. В TypeScript это представляется аналогично сигнатуре вызова функции, но с именем new, например:

type F = (x: string) => number[];
type C = new(x: string) => number[]; 

Тип F представляет функцию, которая принимает параметр string и создает массив number с, в то время как тип C представляет конструктор функцию, которая принимает string параметр и выдает массив number s:

declare const f: F;
declare const c: C;
const arr1 = f("hey"); // number[]
const oops1 = new f("hey"); // error, f is not newable
const arr2 = new c("hey"); // number[]
const oops2 = c("hey"); // error, c is not callable

Распространен статический интерфейс, который также является интерфейсом конструктора; все class статические интерфейсы являются интерфейсами конструктора. Но не каждый статический интерфейс является интерфейсом конструктора. Интерфейс JQueryStatic является примером того, которого нет. Чтобы получить экземпляр JQuery из объекта JQueryStatic, вы вызываете его как функцию (именно это означает подпись (query: string): JQuery;).

В этом и заключается основное различие между парой JQueryStatic / JQuery и парой RegExpConstructor / RegExp и окончание основного ответа на вопрос.


* Вернуться к String морщинке. Тип с именем String специально относится к объекту , созданному путем вызова оператора new в конструкторе String. Существует также тип с именем string (с нижним регистром '), который относится к типу данных примитив . Практически все строки, с которыми вы имеете дело, являются такими примитивами типа string, тогда как String является относительно необычным объектом-оболочкой , который содержит значение string. A string и String в основном взаимозаменяемы:

const stringPrimitive = "hello"; // type is string
const stringObject = new String("hello"); // type is String
console.log(stringPrimitive+"!"); // "hello!"
console.log(stringObject+"!"); // "hello!"
console.log(stringPrimitive.charAt(4)); // "o"
console.log(stringObject.charAt(4)); // "o"

за исключением случаев, когда они не являются взаимозаменяемыми:

console.log(typeof stringPrimitive); // "string"
console.log(typeof stringObject); // "object"
console.log(stringPrimitive instanceof String); // false
console.log(stringObject instanceof String); // true

Ситуация становится еще более запутанной, когда вы понимаете, что StringConstructor также может быть вызван как функция и создает примитив string:

console.log(typeof "hey"); // "string"
console.log(typeof new String("hey")); // "object"
console.log(typeof String("hey")); // "string"

Так что это довольно беспорядок. Правило здесь в значительной степени всегда использовать string; никогда не используйте String. И именно поэтому я изменил пример кода с String на всегда-объект RegExp, для которого не существует примитивного типа данных (typeof /foo/ === "object"), который мешал бы.


Хорошо, надеюсь, это поможет. Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...