There's a function I use in testing:
export function cast<T>(obj: any, klass: new (...args: any[]) => T): T {
if (!(obj instanceof klass)) {
throw new Error(`Not an instance of ${klass.name}: ${obj.constructor.name}`);
}
return obj;
}
which is used like this.
interface I {...}
class C implements I {...};
const i: I = ...;
const c = cast(i, C);
However, if I try to use generics, the compiler complains string
in the call.
interface I<T> {...}
class C<T> implements I {...};
const i: I<string> = ...;
const c = cast(i, C<string>);
~~~~~~
Type 'any' has no signatures for which the type argument list is applicable. ts(2635)
Any thoughts on how I can make a version of cast that is friendly to generics? Even if it can't verify that instances in i
are of type string
, finding a way to get it to compile would be useful (otherwise I have to use as
anyway.)
TypeScript cast type using generics and constructor? looks close, but seems bound to a specific type hierarchy.
CodePudding user response:
Answer
Requires Instantiation expression pattern added in TS4.7.0 https://github.com/microsoft/TypeScript/pull/47607. C<string>
fails to compile because before 4.7 there was no support to instantiate generic functions/classes with a specific type parameter. Short of upgrading version, there isn't really a terse or compact workaround (I know of). I have done a workaround for instantiation expressions here, but for functions, and it may or may not be of any use.
Workaround
You'll have to explicitly declare C<string>
as the generic parameter.
const c = cast<C<string>>(i, C)
Or you can create an instantiator function (although this will only work on class C)
export const I = <T>(
Base: new (...args: any[]) => C<T>
) => {
return Base
}
export const CString = I<string>(C)
const c = cast(i, I<string>(C))
const c2 = cast(i, CString)