根据 TypeScript 中的现有对象创建 TypeSafe 对象字面量 [英] Create TypeSafe Object literal based upon existing object in TypeScript

查看:19
本文介绍了根据 TypeScript 中的现有对象创建 TypeSafe 对象字面量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下函数:

function keysToValuesClone<TObject extends {}>(keysAndValuesObj: TObject) {
    const clone = Object.assign({}, keysAndValuesObj);

    Object.keys(clone).forEach(key => clone[key] = key);

    const frozen = Object.freeze(clone);

    return (<any>frozen) as Readonly<{ // The `any` is regrettable... see below
        [K in keyof typeof clone]: K
    }>;
}

我可以为这个函数提供一个像这样的对象字面量:

I can supply this function with an object literal like so:

const myHeaders = keysToValuesClone({
    'a property with spaces': '',
    'and another': '',
});

并且生成的 myHeaders 将具有以下类型:

And the produced myHeaders will have this type:

Readonly<{
    'a property with spaces': "a property with spaces";
    'and another': "and another";
}>

我喜欢这个函数的实用性,但编写它在我脑海中引发了两个问题:

I like the utility of this function but writing it has raised 2 questions in my mind:

如果我将 keysToValuesClone 的最后一行更改为:

If I change the final line of keysToValuesClone to this:

    return frozen as Readonly<{
        [K in keyof typeof clone]: K
    }>;

然后我遇到了以下错误:

Then I'm landed with the following error:

Type 'Readonly<TObject & {}>' cannot be converted to type 'Readonly<{ [K in keyof (TObject & {})]: K; }>'.
  Type '(TObject & {})[P]' is not comparable to type 'P'.

我已经尝试以多种不同的方式说服编译器在没有 any 类型断言的情况下允许这个函数,但所有尝试都失败了.这是可以改进的吗?你能说服编译器不诉诸any吗?

I've tried convincing the compiler in many different ways to allow this function without the any type assertion but all attempts fail. Is this improvable? Can you convince the compiler to come along for the ride without resorting to any?

我很想写这段代码:

const myHeaders = keysToValuesUsingArray([
    'a property with spaces',
    'and another'
]);

即提供一个字符串数组并使其输出与上述相同.Codewise 我可以这样实现:

i.e. supply a string array and have it output the same as the above. Codewise I can achieve this like so:

function keysToValuesArray(keys: string[]) {
    const obj = keys.reduce((objInTheMaking, key) => ({ [key]: key, ...objInTheMaking  }), {});

    return obj;
}

但这会产生 {} 类型.有没有办法在类型系统中找到这里发生的事情的线索?

But this produces the type {}. Is there a way to clue in the type system to what is happening here?

我玩过 keyof/typeof/Tuple 等,但都无济于事.所以我的朋友们我求助于你.

I've played with keyof / typeof / Tuple etc all to no avail. And so my friends I turn to you.

教育我.

太棒了提香;非常感谢!我最终通过只读调整使用了您的阵列解决方案;见下文:(我现在莫名其妙地非常喜欢Object.freeze;这是我的果酱)

That's awesome Titian; thanks so much! I ended up rolling with your array solution with Readonly tweaks; see below: (I've unaccountably got a lot of love for Object.freeze right now; it's my jam)

function keysToValuesArray<T extends string>(keys: T[]): Readonly<{ [P in T] : P}> {
    const obj = keys.reduce((objInTheMaking, key) => ({ [key]: key, ...objInTheMaking  }), {} );

    const frozen = Object.freeze(obj);

    return frozen as Readonly<{ [P in T] : P}>;
}

再次感谢!

推荐答案

当然可以从 string 数组创建对象,如果我们使用泛型类型参数来表示字段名称:

Sure you can create an object from a string array, if we use a generic type parameter to represent the fields names:

function keysToValuesArray<T extends string>(keys: T[]) : { [P in T] : P}{
    const obj = keys.reduce((objInTheMaking, key) => ({ [key]: key, ...objInTheMaking  }), {} );
    return obj as { [P in T] : P};
}
const myHeaders2 = keysToValuesArray([
    'a property with spaces',
    'and another'
]);

为了避免强制转换为 any,您可以将 clone 直接输入为 any{ [n: string]:任何 } 但这不会好得多.在构建这些对象时,它们无论如何都不符合类型,因此没有太大区别.我通常认为调用站点是类型安全的,这更重要.

As o avoiding a cast to any, you can type clone directly as any or { [n: string]: any } but that will not be much better. While building these objects they don't conform to the type anyway, so it doesn't make much difference. I usually take solace in the idea that the call site is type safe which is more important.

function keysToValuesClone<TObject extends {}>(keysAndValuesObj: TObject) {
    const clone : { [n: string]: any } = Object.assign({}, keysAndValuesObj);

    Object.keys(clone).forEach(key => clone[key] = key);

    const frozen = Object.freeze(clone);

    return frozen as Readonly<{ 
        [K in keyof typeof clone]: K
    }>;
}

这篇关于根据 TypeScript 中的现有对象创建 TypeSafe 对象字面量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆