Какого типа должен быть суперкласс при использовании шаблона фабрики подкласса в TypeScript? - PullRequest
0 голосов
/ 24 октября 2018

Я пытаюсь реализовать шаблон фабрики подклассов в TypeScript 3.x.Рассмотрим этот тест:

import { expect } from 'chai'

describe('subclass factory', () => {
  it('should work', () => {
    interface INameable {
      name?: string
    }

    const name = 'I am a Nameable!'

    function nameableSubclassOf<T> (superclass) {
      return class extends superclass implements INameable {
        name?: string = name
      }
    }

    class Foo {}

    const NameableFoo = nameableSubclassOf(Foo)
    const nameableFoo = new NameableFoo()

    expect(nameableFoo).to.be.instanceOf(Foo)
    expect(nameableFoo.name).to.be.ok
    expect(nameableFoo.name).to.equal(name)
  })
})

Сбой компиляции со следующим сообщением:

TSError: ⨯ Невозможно скомпилировать TypeScript:

src / test / subclass-factory.ts (11,37): ошибка TS7006: параметр 'суперкласс' неявно имеет тип 'any'.

Как изменить приведенный выше код для успешной компиляции и вернуть class, то естьподкласс T, который также объявляет, что он реализует INameable?

Ответы [ 2 ]

0 голосов
/ 24 октября 2018

Я думаю, вы можете либо сказать Typescript, что superclass может new, синтаксис для которого кратко упоминается в руководстве по TypeScript :

// "new (...args: any[]) => any" means the constructor takes any number of arguments 
// and returns anything

function nameableSubclassOf<C extends new (...args: any[]) => any>(superclass: C) {
  return class extends superclass implements INameable {
    name?: string = name
  }
}

Это должно позволить компилятору выводить полезный, но довольно непрозрачный тип для возвращаемого значения nameableSubclassOf:

const NameableFoo = nameableSubclassOf(Foo)
// const NameableFoo: {
//   new (...args: any[]): nameableSubclassOf<typeof Foo>.(Anonymous class);
//   prototype: nameableSubclassOf<any>.(Anonymous class);
// } & typeof Foo ?

const nameableFoo = new NameableFoo();
// const nameableFoo: nameableSubclassOf2<typeof Foo>.(Anonymous class) & Foo; ?

const fooName = nameableFoo.name;
// const fooName: string | undefined; ?

... или, если вы хотите более явный тип, который не 'Если вы полагаетесь на анонимные классы, вы можете использовать обобщенные значения для указания суперкласса и условные типы для извлечения аргумента конструктора и возврата из него типов:

function nameableSubclassOf<C extends new (...args: any[]) => any>(
  superclass: C
): C extends new (...args: infer A) => infer T ? new (...args: A) => T & INameable : never;
function nameableSubclassOf(
  superclass: new (...args: any[]) => any
): new (...args: any[]) => INameable {
  return class extends superclass implements INameable {
    name?: string = name
  }
}

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

Это более многословно, но когда вы используете его, вы получаете более приятные типы:

const NameableFoo = nameableSubclassOf(Foo)
// const NameableFoo: new () => Foo & INameable ?

const nameableFoo = new NameableFoo()
// const nameableFoo: Foo & INameable ?

const fooName = nameableFoo.name
// const fooName: string | undefined ?

Надеюсь, один из нихпомогает.Удачи!

0 голосов
/ 24 октября 2018

Сюда?

function nameableSubclassOf(superclass: {new(): any}) {
...