首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用类属性中定义的泛型类型?

如何使用类属性中定义的泛型类型?
EN

Stack Overflow用户
提问于 2021-07-02 19:14:32
回答 1查看 46关注 0票数 0

我想创建自己的类控制器来处理快速请求。

我所拥有的:

控制器:

代码语言:javascript
复制
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>;
}

GetCatsController:

代码语言:javascript
复制
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

代码语言:javascript
复制
import { IsInt, IsOptional } from 'class-validator';
import { Type } from 'class-transformer';

export class GetCatsQuerySchema {
  @Type(() => Number)
  @IsInt()
  @IsOptional()
  age?: string;
}

transform-validate函数:

代码语言:javascript
复制
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

代码语言:javascript
复制
class CatsController extends Controller<GetCatsQuerySchema> {

相反,我希望找到一种在类属性中定义QuerySchema的方法:

代码语言:javascript
复制
export class CatsController extends Controller {
  querySchema = GetCatsQuerySchema;

有可能吗?

EN

回答 1

Stack Overflow用户

发布于 2021-07-02 19:30:51

如果类是泛型的,则必须显式指定类型参数。这是我绝对会选择的方法。

现在,如果类不是泛型,则不必指定泛型类型参数。唯一的问题是你需要而不是QuerySchema找到一种方式来说“使用派生类中的任何类型”。幸运的是,typescript支持多态this,这基本上意味着无论当前的类型是什么。我们可以使用多态this类型来提取派生类型中的querySchema的实际类型:

代码语言:javascript
复制
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;
}

Playground Link

这似乎是可行的,但由于条件类型的原因,您可能迟早会遇到赋值问题,所以我将坚持使用泛型方法。因此,使用风险自负。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68224225

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档