基元的特例泛型类型参数
Special-case generic type parameters for primitives
背景
我在 TypeScript 中有以下泛型 class,它接受构造函数和回调 作为构造函数参数:
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
...
在这里,我实质上是将泛型类型参数定义为构造函数参数。这样做的原因是我需要在运行时知道泛型类型,而这种方法可以做到这一点。
问题
但是,我 运行 遇到了原始类型的问题。基本上,当我执行以下操作时:
var dataPortN = new InputDataPort(Number, num => console.log(num));
num
是 Number
类型,TypeScript 编译器不允许将 Number
分配给 number
(显然,number
不能指定为 type
) 的参数):
var myNum: number;
// ERROR: 'number' is a primitive, but 'Number' is a wrapper object ...
var dataPortM = new InputDataPort(Number, num => myNum = num);
在这种情况下(以及其他primitive/wrapper类型的情况),我想使用相应的基本类型作为泛型类型参数T
。
本质上,我正在寻找以下类型定义:
- 如果
T
是数字,那么数字
- 否则如果
T
是字符串,则字符串
- 否则如果
T
是布尔值,则布尔值
- 其他
T
原样
据我所知,方法重载在这里是不够的,因为它不提供 class 级别的泛型类型。
虽然我不确定问题中描述的要求是否完全可行,但一个可靠的解决方法是为每个基本类型专门化基础 InputDataPort
class。
例如,要支持number
,可以这样做:
class InputNumberDataPort extends InputDataPort<number> {
constructor(listener?: (data: number) => void) {
super(Number as any, listener);
}
}
与 InputStringDataPort
、InputBooleanDataPort
等其余部分相同。这种方法将为 TypeScript 编译器提供正确的泛型类型,并在运行时提供正确的相应构造函数引用。
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()
})
背景
我在 TypeScript 中有以下泛型 class,它接受构造函数和回调 作为构造函数参数:
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
...
在这里,我实质上是将泛型类型参数定义为构造函数参数。这样做的原因是我需要在运行时知道泛型类型,而这种方法可以做到这一点。
问题
但是,我 运行 遇到了原始类型的问题。基本上,当我执行以下操作时:
var dataPortN = new InputDataPort(Number, num => console.log(num));
num
是 Number
类型,TypeScript 编译器不允许将 Number
分配给 number
(显然,number
不能指定为 type
) 的参数):
var myNum: number;
// ERROR: 'number' is a primitive, but 'Number' is a wrapper object ...
var dataPortM = new InputDataPort(Number, num => myNum = num);
在这种情况下(以及其他primitive/wrapper类型的情况),我想使用相应的基本类型作为泛型类型参数T
。
本质上,我正在寻找以下类型定义:
- 如果
T
是数字,那么数字 - 否则如果
T
是字符串,则字符串 - 否则如果
T
是布尔值,则布尔值 - 其他
T
原样
据我所知,方法重载在这里是不够的,因为它不提供 class 级别的泛型类型。
虽然我不确定问题中描述的要求是否完全可行,但一个可靠的解决方法是为每个基本类型专门化基础 InputDataPort
class。
例如,要支持number
,可以这样做:
class InputNumberDataPort extends InputDataPort<number> {
constructor(listener?: (data: number) => void) {
super(Number as any, listener);
}
}
与 InputStringDataPort
、InputBooleanDataPort
等其余部分相同。这种方法将为 TypeScript 编译器提供正确的泛型类型,并在运行时提供正确的相应构造函数引用。
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()
})