Vâng, chúng ta hãy nhìn vào định nghĩa của ErrorConstructor
:
interface ErrorConstructor {
new(message?: string): Error; // is a constructor
(message?: string): Error; // is callable
readonly prototype: Error; // has an Error prototype
}
Vì vậy, một ErrorConstructor
nhu cầu trở thành một nhà xây dựng mà có một đối số chuỗi tùy chọn, và nó cũng cần phải được callable như một chức năng, và nó cần phải có một mẫu thử nghiệm Error
. Chúng ta hãy nhìn vào lớp FooError
của bạn:
class FooError extends Error {
}
FooError.prototype; // okay, type FooError
new FooError("okay"); // okay
FooError("oops"); // error
Nó có một nguyên mẫu FooError
, mà là tốt kể từ FooError
là một subtype của Error
. Nó là một hàm tạo, và nó chấp nhận một đối số vì nó chống lại siêu lớp Error
, là một ErrorConstructor
. Nhưng nó không được gọi là một hàm. Vì vậy, FooError
không phải là ErrorConstructor
.
Tại thời điểm này, bạn cần phải quyết định xem bạn thực sự chăm sóc về nó là một ErrorConstructor
. Bạn có định gọi nó là FooError('msg')
thay vì new FooError('msg')
? Tôi nghi ngờ điều đó. Đối với vấn đề đó, bạn có quan tâm rằng nhà xây dựng của prototype
là loại FooError
? Chắc là không. Trong trường hợp đó, không sử dụng ErrorConstructor
. Thay vào đó, sử dụng giao diện sau đó chỉ quan tâm đến trở thành một nhà xây dựng không bắt buộc-one-string-arg:
interface NoFrillsErrorConstructor {
new(message?: string): Error;
}
Bây giờ mã của bạn sẽ làm việc:
const map = new Map<NoFrillsErrorConstructor, any> ([
[Error, 'do this'],
[FooError, 'do that']
])
Và tất cả sẽ tốt, càng lâu càng bạn chỉ sử dụng phím của bản đồ như cấu trúc:
map.forEach((val, err) => {
err.prototype; // exists, but is type any. Who cares, right?
new err(); // okay
err(); // not okay
})
(Nếu bạn làm chăm sóc về FooError
phù hợp với ErrorConstructor
, có thể được sắp xếp, nhưng nó hơi khó chịu hơn một chút. Hãy cho tôi biết nếu bạn muốn tôi xây dựng.)
Dù sao, hy vọng điều đó sẽ hữu ích; chúc may mắn!