You are getting typeof Err
because classes are special values in TypeScript. Like enums
they can be used as a type and as a value.
Consider next example:
class A extends Error { }
class B extends Error { }
const a = (arg: A) => arg
a(new A()) // ok
a(A) // error
/////////////////////////////
const b = (arg: typeof A) => arg
b(new A()) // error
b(A) // ok
If you expect argument to be a type of class instance, use function a
.
If you expect argument to be a type of class itself, use function b
.
If you want to create MustExtendsSomeClass
, I believe you can just use typeof AnyClass
:
class Animal {
tag = 'animal'
}
class Dog extends Animal { }
class Cat extends Animal { }
class Ant { }
type MustExtendsAnimal = typeof Animal;
const e = <T extends MustExtendsAnimal>(error: T) => e
e(Dog) // ok
e(Cat) // ok
e(Ant) // error
As you might have noticed, Ant
is unassignable and it is expected behaviour.
But why it does not work with Error class ?
class A extends Error { }
type MustExtendsAnimal = typeof Error;
const e = <T extends MustExtendsAnimal>(error: T) => e
e(A) // error
Because it is built-in class with non standard type signature for classes:
interface Error {
name: string;
message: string;
stack?: string;
}
interface ErrorConstructor {
new(message?: string): Error;
(message?: string): Error;
readonly prototype: Error;
}
declare var Error: ErrorConstructor;
It can be used with new
keyword new Error('custom error')
and as a regular function Error(123)
I'd willing to bet that this question is relative to our problem.
If you want to represent a class type which extends Error
class, you should make sure that calling it without new
will create Error
class instance.
Smth like that:
const Err = function (this: Error, message?: string) {
if (new.target) {
// If this function was called with new keyword
this.message = message || ''
this.name = 'some name'
// some inheritance logic
}
// some inheritance logic
return new Error('custom error')
}
But this function does not work either:
const Err = function (this: Error, message?: string) {
if (new.target) {
// If this function was called with new keyword
this.message = message || ''
this.name = 'some name'
// some inheritance logic
}
// some inheritance logic
return new Error('custom error')
}
type MustExtendsAnimal = typeof Error;
const e = <T extends MustExtendsAnimal>(error: T) => e
e(Err) // error
because TS does not recognize this function as a constructor.
This is why you cant create such type for Error
class.
I believe you can use this solution:
type Overload<T extends InstanceType<typeof Error>> = ((this: Error, message?: string | undefined) => T) & { new(message?: string | undefined): T }
class A extends Error { }
const Err = A as Overload<A>
type MustExtendsAnimal = typeof Error;
const e = <T extends MustExtendsAnimal>(error: T) => e
e(Err) // error