考虑以下功能:
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
}>;
}
我可以为该函数提供一个对象常量,如下所示:
const myHeaders = keysToValuesClone({
'a property with spaces': '',
'and another': '',
});
产生的myHeaders
将具有以下类型:
Readonly<{
'a property with spaces': "a property with spaces";
'and another': "and another";
}>
我喜欢此功能的实用程序,但编写它却引起了两个问题:
any
?如果我将keysToValuesClone
的最后一行更改为此:
return frozen as Readonly<{
[K in keyof typeof clone]: K
}>;
然后我遇到以下错误:
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
?
string[]
构造这种类型?我很想写这段代码:
const myHeaders = keysToValuesUsingArray([
'a property with spaces',
'and another'
]);
即提供一个字符串数组,并使其输出与上述相同。从代码上来说,我可以这样实现:
function keysToValuesArray(keys: string[]) {
const obj = keys.reduce((objInTheMaking, key) => ({ [key]: key, ...objInTheMaking }), {});
return obj;
}
但这会产生类型{}
。在类型系统中是否有办法了解这里发生的事情?
我玩过keyof / typeof / Tuple
等都无济于事。因此,我的朋友们求助于您。
教育我。
太棒了,提香;非常感谢!我最终通过Readonly调整介绍了您的阵列解决方案。见下文:(我现在Object.freeze
真是爱不释手,这是我的麻烦)
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}>;
}
再次感谢!
答案 0 :(得分:2)
如果我们使用通用类型参数表示字段名称,那么可以确保从string
数组创建对象:
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]: any }
,但这不会好得多。在构建这些对象时,它们无论如何都不符合类型,因此并没有太大的区别。我通常对呼叫站点是类型安全的想法感到安慰,这更重要。
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
}>;
}