Расширение типа при расширении класса ES6 с помощью декоратора TypeScript - PullRequest
0 голосов
/ 26 февраля 2019

Я пытаюсь украсить класс декоратором (стиль a-la-angular ) и добавить к нему методы и свойства.

это мой пример украшенного класса:

@decorator
class Person{

}

и это декоратор:

const decorator = (target)=>{
    return class New_Class extends target {
        myProp:string
    }
}

, но myProp не является известным свойством Person:

person.myProp //Error - myProp does not exist on type Person

Как я могу украсить класс машинописи и сохранить завершение типа, безопасность типов и т. Д.?

Ответы [ 3 ]

0 голосов
/ 27 февраля 2019

В дополнение к ответу jcalz , возвращаясь к определению Pattern Decorator , он не меняет интерфейс / контракты своей цели.Это не просто терминология.Декораторы TypeScript имеют сходство с аннотациями Java и атрибутами .NET, которые соответствуют факту не менять интерфейс: они просто добавляют метаданные.

Класс mixin - хороший кандидат для решения вашего вопроса.Но лучше не использовать «декоратор» в своем названии, чтобы избежать путаницы.

0 голосов
/ 04 апреля 2019

Я нашел решение для реализации своего рода множественного наследия (на самом деле это каскадно), но стоит взглянуть.

Предположим, у вас есть класс Base с несколькими свойствами и методами:

class Base {
    tableName = 'My table name';
    hello(name) {
     return `hello ${name}`;
    }
}

И вы хотите, чтобы класс расширял Base, но вы также определили некоторые свойства, которые хотите использовать повторно.Для этого мы будем выполнять следующую функцию:

type Constructor<T = {}> = new (...args: any[]) => T;
function UserFields<TBase extends Constructor>(Base: TBase) {
    return class extends Base {
        name: string;
        email: string;
    };
}

Теперь мы можем создать класс, который расширяет Base и расширяет UserFields, а служба языка машинописи найдет свойства из обоих классов.Он эмулирует множественное наследие, но на самом деле это каскад.

class User extends UserFields(Base) { }
const u = new User();
u.tableName = 'users'; // ok
u.name = 'John'; // ok

Таким образом, вы можете повторно использовать функцию UserFields с любыми другими классами.Ярким примером этого является то, что если вы хотите представить объект User на стороне клиента как «чистый» объект и иметь доступные поля, а затем у вас есть объект UserDb с подключениями к базе данных, и любые другие методы на стороне сервера могут иметь те же полятоже.Мы определяем поля базы данных только один раз!

Еще одна хорошая вещь - вы можете связать mixins mixin1 (mixin2 .... (Base)), чтобы иметь столько атрибутов, сколько вы хотите иметь в одном классе.

Все ожидают, что атрибуты декоратора будут видны при машинописи, но между тем это хорошее решение.

0 голосов
/ 26 февраля 2019

Есть проблема GitHub по этому с лотами обсуждения.Я думаю, что краткое изложение этого: декораторы не видоизменяют тип class (большая часть дискуссии о том, должен ли или не * 1011(да будет так), и поэтому вы не можете делать это так, как хотите, например:

const decorator = (target: new (...args: any) => any) => {
  // note I'm extending target, not Person, otherwise you're not
  // decorating the passed-in thing
  return class New_Class extends target {
    myProp!: string
  }
}

@decorator
class Person {
  noKnowledgeOfMyProp: this['myProp'] = "oops"; // error
}

declare const p: Person;
p.myProp; // error, uh oh

Вместо этого вы можете просто использовать декоратор как простой mixin функция, и Person расширяет возвращаемое значение этого.В итоге вы получите два определения класса ... одно, которое передается в decorator, а другое - в ваш новый класс.«Внутренний» класс (переданный в decorator()) все еще не знает о добавленных реквизитах, но «внешний» класс знает:

class Person extends decorator(class {
  innerProp: string = "inner";
  noKnowledgeOfMyProp: this['myProp'] = "oops"; // error
}) {
  outerProp: string = "outer"
  hasKnowledgeOrMyProp: this['myProp'] = "okay"; // okay
}

declare const p: Person;
p.myProp; // okay

Помогает ли это?Удачи!

...