打字稿的通用“投射”功能

时间:2018-12-19 17:01:16

标签: typescript

我想编写一个函数,使我可以将所有对象属性(我只有纯对象)转换为特定类型。目前,我只想强制转换为基元booleanstringnumber。遵循以下原则:

function castMembers<TObject extends object, TValue extends boolean | string | number>(instance: TObject, type: TValue): Record<keyof TObject, TValue> {
    Object.keys(instance).reduce((result, k) => {
        // ERROR
        result[k] = new type(instance[k]).valueOf();
        return result;
    }, {});
}

将来,只要满足我的接口定义(它必须包含valueOf函数),我可以将其类型扩展到任何类型。所以...

我很清楚,我不能真正基于泛型类型实例化新对象,因为它们仅用于编译时。这就是为什么我添加了第二个参数,以便可以提供用于铸造的构造函数的原因。但是我遇到错误,或者我无效地写了我的TValue通用参数或第二个参数的类型。

如何编写这样的功能?

编辑

我尝试引入构造函数接口,这使我非常接近,但仍然出现错误:

interface IConstructor<TValue> {
    new(value?: any): TValue;
}

function castMembers<TObject extends object, TValue extends number | string | boolean>(instance: TObject, toType: IConstructor<TValue>): Record<keyof TObject, TValue> | never {
    return (Object.keys(instance) as (keyof TObject)[]).reduce((result, key) => {
        result[key] = new toType(instance[key]).valueOf() as TValue;
        return result;
    }, {} as Record<keyof TObject, TValue>);
}

Here's some code to play with

1 个答案:

答案 0 :(得分:1)

实际上,我在玩游戏时自己解决了它。我终于做到了。
这是代码。

interface IPrimitiveLike<T> {
    valueOf(): T;
}

interface IConstructor<T> {
    readonly prototype: IPrimitiveLike<T>;
    new(value?: any): IPrimitiveLike<T>;
}

export default function castMembers<TObject extends object, TValue extends IPrimitiveLike<unknown>>(instance: TObject, toType: IConstructor<TValue>): Record<keyof TObject, TValue> | never {
    return (Object.keys(instance) as (keyof TObject)[]).reduce((result, key) => {
        result[key] = new toType(instance[key]).valueOf() as TValue;
        return result;
    }, {} as Record<keyof TObject, TValue>);
}

此代码将正确执行:

let obj = {
    text: 'test',
    value: 123,
    negative: 0,
    bool: true
};
castMembers(obj, Boolean); // { text: true, value: true, negative: false, bool: true }
castMembers(obj, String); // { text: 'test', value: '123', negative: '0', bool: 'true' }
castMembers(obj, Custom); // also works if your custom class defines a valueOf() function

我唯一不完全确定的是使用unknown类型。当然可以用any代替它,但理想情况下,它应该是与valueOf返回类型相关的原始类型。