You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从泛型获取实例类型?修复TypeScript测试助手类型问题

修复TypeScript类型错误:测试助手函数回调参数类型识别问题

问题背景

现有以下错误类定义:

export class ValidationFailedError extends Error {
  public errors: ValidationError[];

  constructor(errors: ValidationError[]) {
    super('Validation errors exist');
    this.errors = errors;
  }
}

export class ValidationError {}
export class DuplicateFileValidationError extends ValidationError {}
export class MultipleTransactionTypesValidationError extends ValidationError {}
export class FileIsPlacedInWrongFolderValidationError extends ValidationError {
  constructor(public fileName: string, public expectedFolder: string, public actualFolder: string) {
    super();
  }
}
export class InvalidTransactionsCountValidationError extends ValidationError {
  constructor(
    public transactionsCount: number,
    public minTransactionsCount: number,
    public maxTransactionsCount: number,
  ) {
    super();
  }
}

为简化Jest测试中对ValidationFailedError的验证,编写了如下测试助手函数,意图验证错误仅包含指定类型的单个ValidationError,并支持自定义断言回调:

function expectValidationFailedErrorContainsSingleError<T extends ValidationError>(
  validationFailedError: ValidationFailedError,
  errorCtor: T,
  customAssertAction?: (err: T) => void,
): void {
  const { errors } = validationFailedError;
  expect(Array.isArray(errors)).toBe(true);
  expect(errors.length).toEqual(1);

  const validationError = errors[0] as T;
  expect(validationError).toBeInstanceOf(errorCtor);

  customAssertAction(validationError);
}

但在测试中使用时,TypeScript无法识别回调参数的实例类型:

expectValidationFailedErrorContainsSingleError(
  err,
  FileIsPlacedInWrongFolderValidationError,
  (validationError) => {
    expect(validationError.fileName).toEqual('test-file.json');
    expect(validationError.actualFolder).toEqual(actualFolder);
    expect(validationError.expectedFolder).toEqual(expectedFolder);
  },
);

抛出错误:

Property 'fileName' does not exist on type 'typeof FileIsPlacedInWrongFolderValidationError'.ts(2339)
Property 'actualFolder' does not exist on type 'typeof FileIsPlacedInWrongFolderValidationError'.ts(2339)
Property 'expectedFolder' does not exist on type 'typeof FileIsPlacedInWrongFolderValidationError'.ts(2339)

鼠标悬停显示validationError的类型为typeof FileIsPlacedInWrongFolderValidationError(类构造函数类型),而非实例类型。

错误原因

原助手函数的泛型参数T extends ValidationError限定的是实例类型,但传入的errorCtor参数是类的构造函数(类型为typeof ValidationError的子类),导致TypeScript错误地将回调参数的类型推断为构造函数类型,而非实例类型。

修复方案

调整泛型参数,让T代表错误实例类型,同时将errorCtor的类型指定为该实例的构造函数类型。修改后的助手函数如下:

function expectValidationFailedErrorContainsSingleError<T extends ValidationError>(
  validationFailedError: ValidationFailedError,
  errorCtor: new (...args: any[]) => T, // 指定为T的构造函数类型
  customAssertAction?: (err: T) => void,
): void {
  const { errors } = validationFailedError;
  expect(Array.isArray(errors)).toBe(true);
  expect(errors.length).toEqual(1);

  const validationError = errors[0] as T;
  expect(validationError).toBeInstanceOf(errorCtor);

  customAssertAction?.(validationError); // 用可选链避免未传回调时报错
}

验证效果

修改后,测试代码中的validationError会被正确推断为FileIsPlacedInWrongFolderValidationError实例类型,能够正常访问fileName、actualFolder、expectedFolder属性,TypeScript不再抛出类型错误。

内容的提问来源于stack exchange,提问作者Andrei Khotko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 18:05:42