Reputation: 31992
I have the following generic class in TypeScript, that accepts a constructor function and a callback as its constructor arguments:
class InputDataPort<T> {
constructor(type: new (...args: any[]) => T, listener?: (data: T) => void);
onUpdate(listener?: (data: T) => void);
// ... lots of other methods depending on the generic type parameter 'T'
}
var dataPortA = new InputDataPort(Array, array => console.log(array)); // T: Array
var dataPortB = new InputDataPort(Date, array => console.log(array)); // T: Date
...
Here, I'm essentially defining the generic type parameter as a constructor argument. The reason it's this way is that I need to know the generic type at runtime, and this approach enables that.
However, I'm running into a problem with primitive types. Basically, when I do the following:
var dataPortN = new InputDataPort(Number, num => console.log(num));
num
is of type Number
, and the TypeScript compiler does not allow assigning a Number
to a number
(obviously, number
cannot be specified as the argument to type
):
var myNum: number;
// ERROR: 'number' is a primitive, but 'Number' is a wrapper object ...
var dataPortM = new InputDataPort(Number, num => myNum = num);
In this case (as well as in the case of other primitive/wrapper types), I would like to use the corresponding primitive types as the generic type parameter T
.
Essentially, I'm looking for the following type definition:
T
is Number, then numberT
is String, then stringT
is Boolean, then booleanT
as-isTo the best of my knowledge, method overloading is not sufficient here, as it doesn't provide a class-level generic type.
Upvotes: 3
Views: 1405
Reputation: 2249
Advanced Types > Conditional Types · TypeScript
type PrimitiveMapped2<T> =
T extends Number ? number :
T extends String ? string :
T extends Boolean ? boolean :
T
class InputDataPort<T> {
constructor(type: new (...args: any[]) => T, listener?: (data: PrimitiveMapped2<T>) => void) {
throw new Error('todo')
}
}
let a = new InputDataPort(Number, num => { // number
num.toFixed()
})
let b = new InputDataPort(Date, date => { // Date
date.getTime()
})
Upvotes: 1
Reputation: 31992
Although I'm not sure if the requirements described in the question are possible at all, one solid workaround is specializing the base InputDataPort
class for each primitive type.
For example, to support number
, one can do:
class InputNumberDataPort extends InputDataPort<number> {
constructor(listener?: (data: number) => void) {
super(Number as any, listener);
}
}
Same for the rest with InputStringDataPort
, InputBooleanDataPort
, and so on. This approach will provide the correct generic types for the TypeScript compiler, and also the correct corresponding constructor references at runtime.
Upvotes: 0