英文:
How to get an instance type from generic type?
问题
我有以下的类:
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
,然后还检查ValidationFailedError.errors
数组中的内容。为了这些测试,我编写了以下的辅助函数,它检查ValidationFailedError
错误是否只包含一个指定的ValidationError
错误,并执行一些自定义的断言,这些断言以回调函数的形式提供,该回调函数以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);
}
上面的函数编译没有问题,但是在我的测试中使用这个函数时出现了问题。请看下面的示例代码,这段代码是我从我的一个测试中摘取的:
expectValidationFailedErrorContainsSingleError(
err,
FileIsPlacedInWrongFolderValidationError,
(validationError) => {
expect(validationError.fileName).toEqual('test-file.json');
expect(validationError.actualFolder).toEqual(actualFolder);
expect(validationError.expectedFolder).toEqual(expectedFolder);
},
);
TypeScript无法识别validationError
参数的类型,它会报出以下3个错误:
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
:
我感觉validationError
的类型不是实例类型,而是类类型。
问题: 有没有办法解决这个问题,这样我就可以在函数中访问fileName
、actualFolder
和expectedFolder
属性了?
英文:
I have the following classes:
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();
}
}
And I have some tests (I'm using Jest) that check if function throws a ValidationFailedError
, and then also checks, what is inside ValidationFailedError.errors
array. For the tests I wrote the following helper function which checks that ValidationFailedError
error contains only one error of provided ValidationError
, and performs some custom asserts provided as a callback function which takes single argument - ValidationError
with expected type as an argument:
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);
}
The function above is compiled fine, however I have an issue with using this function in my tests. Please take a look at the example below, I took this piece of code from one of my tests:
expectValidationFailedErrorContainsSingleError(
err,
FileIsPlacedInWrongFolderValidationError,
(validationError) => {
expect(validationError.fileName).toEqual('test-file.json');
expect(validationError.actualFolder).toEqual(actualFolder);
expect(validationError.expectedFolder).toEqual(expectedFolder);
},
);
Typescript does not recognize the type of validationError
parameter, it raises the following 3 errors:
> 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)
When I hover on validationError
parameter, I see that the type of this parameter is typeof FileIsPlacedInWrongFolderValidationError
:
I feel like the type of validationError
is not an instance type, it's a class type.
The Question: Is it possible to fix this issue, so I can access the properties fileName
, actualFolder
and expectedFolder
in the function?
答案1
得分: 1
假设您不打算调用errorCtor
(因为您无法确定它在这里是否有参数或有多少个参数),此类型在TS Playground上没有显示任何警告:
function expectValidationFailedErrorContainsSingleError<T extends ValidationError>(
validationFailedError: ValidationFailedError,
errorCtor: new (...args: never[]) => 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); // 注意可选链的使用
}
const err = new ValidationFailedError([
new FileIsPlacedInWrongFolderValidationError('test-file.json', '/etc/ts-test', '/etc/ts_test')
])
expectValidationFailedErrorContainsSingleError(
err,
FileIsPlacedInWrongFolderValidationError,
(validationError) => {
const { fileName, actualFolder, expectedFolder } = validationError
expect(fileName).toEqual('test-file.json');
expect(actualFolder).toEqual('/etc/ts-test');
expect(expectedFolder).toEqual('/etc/ts_test');
},
);
英文:
Assuming you don't plan on calling the errorCtor
(since you can't know if or how many arguments it has here), this typing doesn't show any warnings on TS Playground:
function expectValidationFailedErrorContainsSingleError<T extends ValidationError>(
validationFailedError: ValidationFailedError,
errorCtor: new (...args: never[]) => T, // Constructor type
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); // Note the optional chaining
}
const err = new ValidationFailedError([
new FileIsPlacedInWrongFolderValidationError('test-file.json', '/etc/ts-test', '/etc/ts_test')
])
expectValidationFailedErrorContainsSingleError(
err,
FileIsPlacedInWrongFolderValidationError,
(validationError) => {
const { fileName, actualFolder, expectedFolder } = validationError
expect(fileName).toEqual('test-file.json');
expect(actualFolder).toEqual('/etc/ts-test');
expect(expectedFolder).toEqual('/etc/ts_test');
},
);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论