#typescript
#typescript
Вопрос:
Кажется, я не могу определить наиболее элегантный способ определения типа анонимных универсальных классов, которые генерируются и возвращаются из функций.
Вот фрагмент, который содержит функцию, которая генерирует анонимные классы, и одну из функций, содержащихся внутри.
Общие элементы необходимы для того, чтобы TypeScript предоставлял подсказки о типе возвращаемого значения и свойстве параметра при использовании функции get.
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;
}
}
};
}
Проблема возникает, когда я пытаюсь фактически создать и сохранить результат этого.
Вот пример создания пользовательского контроллера.
export interface UserQueryParams {
id?: number;
username?: string;
}
const UserController = createController<User, UserQueryParams>(User);
Теперь экземпляры UserController можно создавать, просто создавая их как обычные классы через new 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
ПОЧТИ работает, но информация о типе теряется. Это выводится как createController<any, any>.(Anonymous class)
. Два any
, используемые для обобщений, означают, что параметр и информация о типе возвращаемого значения отсутствуют.
Используя
const temp = new UserController();
// In the class..
public controller: typeof temp;
Это работает! Однако крайне некрасиво создавать временные экземпляры КАЖДОГО контроллера, который я мог бы сгенерировать.
Есть ли какой-либо другой способ ввести controller
правильно?
Ответ №1:
Я нашел решение, которое считаю достаточно элегантным, используя InstanceType
из TypeScript 2.8
import { UserController } from '@/controllers';
class Client {
public controller: InstanceType<typeof UserController>;
public constructor() {
this.controller = new UserController();
}
}
Я даже могу экспортировать тип из того же файла, используя то же имя, что и сгенерированный UserController
класс, и TypeScript, похоже, может сделать вывод, что экспортируются как тип, так и класс.
// 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!
}
}