我似乎无法确定最优雅的方法来确定从函数生成和返回的匿名泛型类的类型。
这是一个片段,其中包含生成匿名类的函数以及其中包含的函数之一。
在使用get函数时,TypeScript必须提供泛型才能提供返回类型和参数属性提示。
export function createController<T, U extends { id?: number }>(cls: new () => T) {
return class extends BaseController {
public constructor() {
super(cls.name.split(/(?=[A-Z])/).join('_').toLowerCase());
}
public async get(params: U): Promise<T | null> {
try {
const { data } = params.id ? await this.http.get(`${this.base}-${params.id}`) : await this.http.get('', { params });
return deserialize(cls, search(data, 'data'));
} catch (err) {
return null;
}
}
};
}
当我尝试实际创建并存储此结果时,就会出现问题。
这里是创建UserController的示例。
export interface UserQueryParams {
id?: number;
username?: string;
}
const UserController = createController<User, UserQueryParams>(User);
现在,可以通过new UserController()
通过像普通类一样简单地创建UserController实例来创建它们。
直接存储它们时没有问题,即
// Correctly inferred as createController<User, UserQueryParams>.(Anonymous class)
const controller = new UserController();
但是,说我想创建一个包含这些控制器的类:
class Client {
public controller: ???
public constructor() {
this.controller = new UserController();
}
}
我似乎无法找出键入属性的最佳方法。
使用public controller: UserController
错误,因为UserController
是一个值而不是类型。
使用public controller: typeof UserController
似乎也不起作用,由于类型不兼容,构造函数分配失败。
使用public controller: typeof UserController.prototype
ALMOST可以,但是类型信息丢失。推断为createController<any, any>.(Anonymous class)
。用于泛型的两个any
表示参数和返回类型信息已消失。
使用
const temp = new UserController();
// In the class..
public controller: typeof temp;
这有效!但是,必须为我可能生成的每个控制器创建临时实例非常丑陋。
还有其他方法可以正确键入controller
吗?
答案 0 :(得分:2)
我使用TypeScript 2.8中的InstanceType
找到了一个我认为足够优雅的解决方案
import { UserController } from '@/controllers';
class Client {
public controller: InstanceType<typeof UserController>;
public constructor() {
this.controller = new UserController();
}
}
我什至可以使用与生成的UserController
类相同的名称从同一文件中导出类型,并且TypeScript似乎能够推断出两者都正在导出Type和Class
// Inside './controllers/index.ts
export const UserController = createController<User, UserQueryParams>(User);
export type UserController = InstanceType<typeof UserController>;
// ... In some other file
// TypeScript understands you are importing the Type AND the Class!
import { UserController } from './controllers';
class Client {
public controller: UserController; // Works as type!
public constructor() {
this.controller = new UserController() // Works as class!
}
}