3

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.

4
  • 1
    I cannot reproduce this TS Playground Link. Can you create a minimal reproducible example? (IE providing a playground link would be best). Commented Jun 26, 2022 at 4:39
  • I'm using Typescript 4.4.4 and with a small change, the minimal example fails to compile. So whatever it is, has changed in between. Specifically, changing from 4.6.4 to 4.7.4 removes the error. Commented Jun 26, 2022 at 5:41
  • 1
    This is because of the Instantiation expression pattern added in TS4.7.0 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. Commented Jun 26, 2022 at 6:02
  • 1
    That sounds about right. Thanks. I'm prevented from upgrading due to downstream dependencies. In the meantime you want to make that an answer, I'll accept it. Commented Jun 28, 2022 at 4:25

1 Answer 1

1

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)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.