typescript - 如何使用类属性中定义的泛型类型?
问题描述
我想创建自己的类控制器来处理快速请求。
我有的:
控制器:
import { Request, Response } from 'express';
import { transvalidate } from './schema/transform-validate';
import { ClassConstructor } from 'class-transformer';
export abstract class Controller<QuerySchema extends object> {
querySchema?: ClassConstructor<QuerySchema>;
query?: QuerySchema;
constructor(
protected readonly req: Request,
protected readonly res: Response,
) {
if (this.querySchema) {
this.query = transvalidate(this.querySchema, this.req.query);
}
}
getQuery(): QuerySchema {
if (!this.query) {
throw new Error(
`Can't get query. Probably the querySchema is not provided.`,
);
}
return this.query;
}
abstract handle(): Promise<unknown>;
}
获取猫控制器:
import { Controller } from './controller';
import { GetCatsQuerySchema } from './schema/query.schema';
export class CatsController extends Controller<GetCatsQuerySchema> {
querySchema = GetCatsQuerySchema;
handle(): Promise<unknown> {
// here this.getQuery() returns transformed to GetCatsQuerySchema and validated req.query object
return Promise.resolve(undefined);
}
}
GetCatsQuerySchema
:
import { IsInt, IsOptional } from 'class-validator';
import { Type } from 'class-transformer';
export class GetCatsQuerySchema {
@Type(() => Number)
@IsInt()
@IsOptional()
age?: string;
}
transform-validate
功能:
import 'reflect-metadata';
import { ClassConstructor, plainToClass } from 'class-transformer';
import { validateSync, ValidationError } from 'class-validator';
export const transvalidate = <T extends object>(
targetClass: ClassConstructor<T>,
rawData: unknown,
) => {
const transformedValue = plainToClass(targetClass, rawData);
const errors = validateSync(transformedValue);
if (errors.length) {
const errorMessages = getAllErrorMessages(errors);
throw new Error(errorMessages[0]);
}
return transformedValue;
};
const getAllErrorMessages = (errors: ValidationError[]) => {
const errorMessages: string[] = [];
errors.map((error) => {
if (error.constraints) {
errorMessages.push(...Object.values(error.constraints));
}
});
return errorMessages;
};
但我不喜欢每个类QuerySchema
在扩展控制器时都应该提供:
class CatsController extends Controller<GetCatsQuerySchema> {
而不是这个,我想找到一种在类属性中定义 QuerySchema 的方法:
export class CatsController extends Controller {
querySchema = GetCatsQuerySchema;
可能吗?
解决方案
如果该类是泛型的,则必须显式指定类型参数。这是我肯定会采用的方法。
现在,如果该类不是泛型的,则不必指定泛型类型参数。唯一的问题是您需要QuerySchema
找到一种方法来代替说“使用派生类中的任何类型”。幸运的是 typescript 支持 polymorphic this
,这基本上意味着当前类型是什么。我们可以使用多态this
类型来提取querySchema
派生类型中的实际类型:
type QuerySchema<T extends {querySchema?: ClassConstructor<object>}> =
InstanceType<Exclude<T['querySchema'], undefined>>
export abstract class Controller {
querySchema?: ClassConstructor<object>;
query?: QuerySchema<this>;
constructor(
protected readonly req: Request,
protected readonly res: Response,
) {
if (this.querySchema) {
this.query = null!;
}
}
getQuery(): QuerySchema<this> {
// ...
}
abstract handle(): Promise<unknown>;
}
export class CatsController extends Controller {
querySchema = GetCatsQuerySchema;
handle(): Promise<unknown> {
this.getQuery().age
this.getQuery().age1 // error
return Promise.resolve(undefined);
}
}
export class GetCatsQuerySchema {
age?: string;
}
这似乎可行,但是由于条件类型,您迟早会遇到可分配性问题,因此我会坚持使用通用方法。所以使用风险自负。
推荐阅读
- google-bigquery - BIGQUERY 使用 ARRAY 对象运行总计
- sql - 我的问题是关于 SQL Server 中的 XML 数据 - 需要了解下面的这个 SQL 函数
- react-native - 无法在外部触摸时禁用弹出对话框和键盘
- android - Android 上统一原生广告中的 IncompatibleClassChangeError
- typescript - 采取歧视性工会的补充
- python - 'string' 的类型不正确(预期为 str,得到 spacy.tokens.doc.Doc)
- algorithm - Matlab中的集体影响算法
- django - 如何将 FCM 令牌发送到 django 应用程序
- vespa - vespa 中文档的自动更新
- angular - 如何将 css 文件与公共和管理区域组件分开?