我有一个类Collection
,它接受另一个类作为参数personClass
。 我想方法add
应该接受包含在类User
构造函数中的参数
class Person {
constructor(public data: object) {
}
}
type GetConstructorParams<T> = T extends {
new (...params: infer Args): any
} ? Args : never
type GetConstructor<Instance> = new (...params: GetConstructorParams<Instance>) => Instance
class Collection<
Instance extends Person
> {
personClass: GetConstructor<Instance>
items: Instance[] = []
constructor(personClass: GetConstructor<Instance>) {
this.personClass = personClass
}
add(...params: GetConstructorParams<Instance>) {
const instance = new this.personClass(...params)
this.items.push(
instance
)
}
}
class User extends Person {
data: {
name: string
}
constructor(name: string) {
super({name})
}
get name() {
return this.data.name
}
}
const collection = new Collection(User)
collection.add('123')
^^^ TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
然后我将尝试将 personClass 作为可选参数。默认情况下,personClass 应等于Person
如何避免此方法中的错误collection.add('123')
TypeScript 中的类实例类型不保留对其构造函数类型的引用。 请考虑以下类:
class Foo {
a: number;
constructor(a: number) {
this.a = a;
}
}
let f: Foo = new Foo(123);
值f
被批注为类型Foo
。 但是,您应该将类型Foo
视为描述类实例形状的interface
,而不是表示"由Foo
构造函数构造"。 实际上,您可以将相同形状的值分配给f
,而无需由 al 的Foo
构造:
f = { a: 456 }; // <-- also acceptable
如果您查看 microsoft/TypeScript#3841,您会发现即使是类实例的constructor
属性也不够强类型,无法确定有关实际构造函数的任何内容。
因此,无法查看类型Foo
并确定名为Foo
的构造函数是否具有类型number
的构造函数参数。 从实例类型转到构造函数类型而不丢失信息是不可能的。GetConstructor<I>
永远不会按照你想要的方式工作。 无法从名为User
的类型转到名为typeof User
的类型。
另一方面,构造函数类型肯定知道它们构造的实例。 因此,给定类型typeof User
的User
构造函数,您可以获取其实例类型。 甚至还为您提供了InstanceType<T>
实用程序类型。
此外,由于您关心的只是实例类型和构造函数参数列表,因此您可以直接在这些类型中使Collection
泛型,而不必关心构造函数类型本身。 假设构造函数参数类型是类似数组的类型A
,实例类型是类似Person
的类型T
。 然后是Collection
的样子:
class Collection<A extends any[], T extends Person>{
personClass: new (...args: A) => T;
items: T[] = []
constructor(personClass: new (...args: A) => T) {
this.personClass = personClass
}
add(...params: A) {
const instance = new this.personClass(...params)
this.items.push(
instance
)
}
}
这一切都编译没有问题。 然后,您可以传入User
并查看所有内容都按预期工作:
class User extends Person {
data!: {
name: string
}
constructor(name: string) {
super({ name })
}
get name() {
return this.data.name
}
}
const collection = new Collection(User)
// const collection: Collection<[name: string], User>
collection.add('123'); // okay
collection
的类型是Collection<[name: string], User>
,对应于User
构造函数采用单个string
参数并生成类型User
的实例。
操场链接到代码