指定对象中键的类型



我试图应用一个函数对数组中对象的所有键执行减法,但我不确定如何指定类型。

function ForwardTimePointDifferenceTransformer<T>(
array: T[],
key: keyof T & number
) {
array.map((value, i, array) => {
try {
value[key] = array[i + 1][key]! - value[key]!;
return value;
} catch (e) {
return value;
}
});
return array;
}

它不允许我做减法,因为类型不是数字。我如何指定键必须是一个数字?

编辑1这是我要传递的一个数组的例子:

[
{
"value": "example", 
"numberValue": 4.498437
},
{
"value": "example", 
"numberValue": 24.3428
},
{
"value": "example", 
"numberValue": 54
},
{
"value": "example", 
"numberValue": 90
},
{
"value": "example", 
"numberValue": 100
}
]

这里有两种使用相同运行时代码的方法,每种方法都有自己的类型系统权衡:

TS Playground链接

第一:

function forwardTimePointDifferenceTransformer <K extends PropertyKey>(
array: Record<K, number>[],
key: K,
): void {
for (let i = 0; i < array.length - 1; i += 1) {
const current = array[i];
const next = array[i + 1];
current[key] = next[key] - current[key];
}
}

这个版本更容易编写和理解,但是它的编译器诊断消息对有问题的代码没有帮助。当使用示例数组和无效的属性名调用时,将产生以下编译器错误:

forwardTimePointDifferenceTransformer(array, 'value');
//                                    ^^^^^
// Argument of type '{ value: string; numberValue: number; }[]' is not assignable to parameter of type 'Record<"value", number>[]'.
//   Type '{ value: string; numberValue: number; }' is not assignable to type 'Record<"value", number>'.
//     Types of property 'value' are incompatible.
//       Type 'string' is not assignable to type 'number'.(2345)

这导致第二个版本:

type FilterKeysByValue<TObject, TValue> = keyof {
[K in keyof TObject as TObject[K] extends TValue ? K : never]: TObject[K];
};
function forwardTimePointDifferenceTransformer <T>(
array: T[],
key: FilterKeysByValue<T, number>,
): void {
for (let i = 0; i < array.length - 1; i += 1) {
type O = Record<typeof key, number>;
const current = array[i] as unknown as O;
const next = array[i + 1] as unknown as O;
current[key] = next[key] - current[key];
}
}

这个版本包含了更复杂的类型和一些断言来克服编译器的限制,但是我认为编译器的诊断更有用:

forwardTimePointDifferenceTransformer(array, 'value');
//                                           ^^^^^^^
// Argument of type '"value"' is not assignable to parameter of type '"numberValue"'.(2345)

运行您的示例产生如下结果:

const array = [
{ value: 'example', numberValue: 4.498437 },
{ value: 'example', numberValue: 24.3428 },
{ value: 'example', numberValue: 54 },
{ value: 'example', numberValue: 90 },
{ value: 'example', numberValue: 100 },
];
console.log('before', array.map(({numberValue}) => numberValue));
forwardTimePointDifferenceTransformer(array, 'numberValue');
console.log('after', array.map(({numberValue}) => numberValue));
before [ 4.498437, 24.3428, 54, 90, 100 ]
after [ 19.844363, 29.6572, 36, 10, 100 ]

目前不可能做到100%干净(因此as unknown as number,参见:https://github.com/microsoft/TypeScript/issues/38646),但我认为目前的解决方案还不错:

// returns only keys from `T` that match type `U`
type FilteredKeys<T, U> = {
[P in keyof T]: T[P] extends U ? P : never;
}[keyof T];
// interface from your example input
interface ForwardTimePointDifference {
value: "string";
numberValue: number;
}
function ForwardTimePointDifferenceTransformer<
// ensure generic input matches shape
T extends ForwardTimePointDifference
>(
array: T[],
// ensure only keys with the type of `number` can be used
key: FilteredKeys<T, number>
) {
array.map((value, i, array) => {
try {
((value[key] as unknown) as number) = array[i + 1][key]! - value[key]!;
return value;
} catch (e) {
return value;
}
});
return array;
}

相关内容

  • 没有找到相关文章

最新更新