Patrik Valkovič
Patrik Valkovič

Reputation: 724

Class type as parameter in TypeScript

I am a bit stuck with the type hinting in TypeScript when the parameter is class of specific type. I am trying to implement an event system and when I use the code from TypeScript playground, everything works fine for pure JavaScript. However, the standard compilation cause Errors of "TS2693: 'C1' only refers to a type, but is being used as a value here." How can I type-hint this method?

function method
<C1 extends BaseClass, C2 extends BaseClass>
(c1: typeof C1, c2: typeof C2, callback: (arg1: C1, arg2: C2) => void){
    // ...
}

Full example:

abstract class BaseClass {}

const dict = {}

function method
<C1 extends BaseClass, C2 extends BaseClass>
(c1: typeof C1, c2: typeof C2, callback: (arg1: C1, arg2: C2) => void){
    dict[c1] = dict[c1] || {};
    dict[c1][c2] = dict[c1][c2] || [];
    dict[c1][c2].push(callback);
}


class MyClass1 extends BaseClass{}
class MyClass2 extends BaseClass{}


method(MyClass1, MyClass2, (arg1: MyClass1, arg2: MyClass2) => {
    console.log("Callback");
});

const c1 = new MyClass1();
const c2 = new MyClass2();
dict[MyClass1][MyClass2][0](c1, c2);

Upvotes: 3

Views: 3261

Answers (1)

stewartmcgown
stewartmcgown

Reputation: 526

Classes in TypeScript don't behave like you think. Check this snippet out, straight from the TS interpreter:

~ ts-node
> class a {}
undefined
> typeof a
'function'

It shows that a is just a function, so that parameter type hint will evaluate to the literal string 'function'!

> function b(param: a) { console.log(param) };
> b(a)
[Function: a]
> b(new a())
a {}

The above code is an example of how to properly use class types as parameters. No need for typeof. In your code it could look something like this:

function method
<C1 extends BaseClass, C2 extends BaseClass>
(c1: C1, c2: C2, callback: (arg1: C1, arg2: C2) => void){
    // ...
}

If what you mean is you want to actually pass the class type itself, then you need to work around TS's reflection limitations. You can pass the type as a function that returns a type:

function method
<C1 extends BaseClass, C2 extends BaseClass>
(c1: new () => C1, c2: new () => C2, callback: (arg1: C1, arg2: C2) => void){
    new c1() instanceof C1; // true
    new c2() instanceof C2 // true
    c1 instanceof C1 // false
}

Tada!

Upvotes: 3

Related Questions