Распознавание полиморфизма между интерфейсами и классами в Typescript - PullRequest
0 голосов
/ 04 июля 2018

В настоящее время я работаю над проектом в TypeScript (версия 2.9.2) и столкнулся с неожиданным полиморфным поведением. В Java и C # интерфейсы определяют полиморфное поведение в той же степени, что и классы, то есть в следующем случае верно, что item1 может иметь тип A, как и то, что он может иметь тип B, и что item2 может иметь тип C, так как оно может иметь тип D:

interface A { }
class B implements A { }
class C { }
class D extends C { }

Но в TypeScript, похоже, это не так. У меня примерно следующая настройка:

interface A {
    new (str: string): Module;
    someFunction(str: string): A;
}

class B implements A {
    constructor(str: string) { /* ... */ }
    someFunction(str: string): B { /* ... */ }
}

Компилятор, похоже, имеет проблему с типом возвращаемого значения B someFunction(), но по моему пониманию полиморфизма, поскольку B реализует A, если функция возвращает что-то типа A тогда он также должен иметь возможность возвращать что-то типа B. При этом не имеет смысла, что что-то должно «иметь тип A», поскольку интерфейсы не могут быть созданы, и являются не более чем нематериальным соглашением или контрактом между классами. Тогда кажется разумным, что если бы A вместо этого был абстрактным классом, полиморфное поведение должно вести себя так, как я ожидаю - и это действительно так, - но в рамках библиотеки, которую я строю, это кажется более подходящим для A быть интерфейсом.

Проблема, с которой сталкивается компилятор, в частности, заключается в следующем в строке, которая объявляет B s someFunction():

[ts]
Property 'someFunction' in type 'B' is not assignable to the same property in base type 'A'.
  Type '(str: string) => B' is not assignable to type '(str: string) => A'.
    Type 'B' is not assignable to type 'A'.
      Types of property 'someFunction' are incompatible.
        Type '(str: string) => B' is not assignable to type '(str: string) => A'.
(method) Project.B.someFunction(str: string): B

Часть проблемы, кажется, заключается в том, что я объявляю конструктор в A. Если я удалю это определение конструктора, проблема будет решена, но мне нужно, чтобы это определение было частью соглашения о том, что в сущности означает тип A.

Учитывая ожидаемое полиморфное поведение, как я могу написать свой интерфейс или вместо этого использовать абстрактный класс? Как мне вызвать это полиморфное поведение?

1 Ответ

0 голосов
/ 09 июля 2018

Мне нужно, чтобы это определение было частью соглашения о том, что оно в основном означает быть типа A

К сожалению, в языке нет поддержки этого. Сигнатура Construct не может быть частью контракта , который объявлен для реализации классом. extends объявляет только часть экземпляра контракта, конструкторы и статические методы являются частью так называемой "статической части" , и нет способа объявить контракт для этого.

TypeScript использует структурную типизацию, поэтому вы можете использовать B всякий раз, когда ожидается интерфейс, который задает сигнатуру конструкции, но этот интерфейс должен быть объявлен отдельно, и соответствие будет проверяться каждый раз, когда B используется в место, нет способа объявить это заранее:

interface AConstructor {
    new (str: string): A;
}

interface A {
    someFunction(str: string): A;
}

class B implements A {
    constructor(str: string) { /* ... */ }
    someFunction(str: string): B { /* ... */ }
}

function f(cls: AConstructor) {
    const instance = new cls('hi');
    instance.someFunction('how are you?');
}

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