如何推断类可以在typescript中示例化

4urapxun  于 2023-06-24  发布在  TypeScript
关注(0)|答案(1)|浏览(183)

有时候我们需要模板方法来构建关于类似事物抽象关系,例如:

abstract class Animal {
  constructor(public head: string, public leg: string, public hand: string) {}
}

class Bird extends Animal {}

class Human extends Animal {}

class Cat extends Animal {}

// more...

相应地,我们还需要一些函数来创建上面的Class的示例:

function createBird(head: string, leg: string, hand: string) {
  return new Bird(head, leg, hand)
}

function createHuman(head: string, leg: string, hand: string) {
  return new Human(head, leg, hand)
}

function createCat(head: string, leg: string, hand: string) {
  return new Cat(head, leg, hand)
}

但我认为这不够优雅,因为里面有很多重复的内容。更糟糕的是,如果我们想改变抽象类'Animal':

abstract class Animal {
  constructor(public head: string, public leg: string, public hand: string, public foot:string) {}
}

我在构造函数中添加了一个参数,接下来,我必须修改每个创建子类示例的函数,这太糟糕了。我的解决方案如下:

function createClass<T extends typeof Animal>(C: T, head: string, leg: string, hand: string) {
  return new C(head, leg, hand)
}

const bird = createClass(Bird, '', '', '')

const human = createClass(Human, '', '', '')

const cat = createClass(Cat, '', '', '')

但是它会提示类型错误,即抽象类不能被示例化。我不知道如何导出正确的类型,因此请求帮助

2skhul33

2skhul331#

最通用的createClass版本根本不关心Animal或特定的构造函数参数类型列表。相反,它将接受任何具体的构造函数对象和该构造函数所期望的参数列表,并返回创建的示例:

function createClass<A extends any[], T extends object>(
    C: new (...a: A) => T, ...args: A
) {
    return new C(...args)
}

该函数是A中的generic,参数列表和T,示例类型。C参数有一个构造签名new (...a: A) => T,这意味着如果你用A类型的参数列表new它,你会得到一个T
这将适用于您的示例:

class Bird extends Animal { }
const bird = createClass(Bird, '', '', '');
class Human extends Animal { }
const human = createClass(Human, '', '', '');
class Cat extends Animal { }
const cat = createClass(Cat, '', '', '');

以及你想使用的任何其他构造函数:

const date = createClass(Date, 1686749440441);
// const date: Date
console.log(date.getFullYear()) // 2023

因此,它将自动适应Animal构造函数参数类型的任何更改,或者如果Animal的任何子类具有不同的构造函数参数类型列表。
如果你真的想限制C输入到Animal构造函数,以防止createClass(Date, 1686749440441);,你可以通过约束T来做到这一点:

function createClass<A extends any[], T extends Animal>( // <-- constrained more
    C: new (...a: A) => T, ...args: A
) {
    return new C(...args)
}

const bird = createClass(Bird, '', '', ''); // okay
const human = createClass(Human, '', '', ''); // okay
const cat = createClass(Cat, '', '', ''); // okay

const date = createClass(Date, 1686749440441); // error!
//   ------------------> ~~~~
// Type 'Date' is missing the following properties from type 'Animal': ...

Playground链接到代码

相关问题