【发布时间】:2021-12-16 23:37:31
【问题描述】:
我正在将一个项目移植到 typescript 中,除了这个简单的实用功能之外,一切都很好:
function mapObject(obj, mapperFn) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => mapperFn(key, value))
);
}
它用于转换对象的键和值,如下所示:
let obj = { a: 1, b: 2 };
let mappedObj = mapObject(obj, (key, value) => [
key.toUpperCase(),
value + 1,
]);
mappedObj; // { A: 2, B: 3 }
edit 为了清楚起见,输出对象的条目值不一定与输入对象中的条目值相同,例如上面的示例可以将数字转换为字符串以生成 { A: '2', B: '3' },或者只为每个数字返回 null:{ A: null, B: null }。另外,为了简单起见,我只考虑输入和输出对象中的字符串键,因为这就是它在我们项目中的使用方式。
我希望mapperFn 保留对象条目的值类型,支持按键类型缩小,就像GetEntryOf 在下面的示例中所做的一样:
type GetEntryOf<T> = {
[K in keyof T]: [K, T[K]];
}[keyof T];
interface TestI {
str: string;
num: number;
}
let entry: GetEntryOf<TestI>;
if (entry[0] === 'str') {
entry[1]; // string
} else if (entry[0] === 'num') {
entry[1]; // number
} else {
entry; // never
}
这是我最接近的,不幸的是我需要过渡到使用key in obj 运算符:
function mapObject<K>(
obj: K,
mapperFn: (key: keyof K, value: K[typeof key]) => [string, unknown]
): Record<string, unknown> {
const newObj = {} as Record<string, unknown>;
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const value = obj[key];
const [newKey, newValue] = mapperFn(key, value);
newObj[newKey] = newValue;
}
}
return newObj;
}
interface TestI {
str: string;
num: number;
}
let test: TestI;
mapObject(test, (key, value) => {
if (key === 'str') {
return [key, value]; // value should be string
} else if (key === 'num') {
return [key, value]; // value should be number
} else {
return [key, value]; // value should be never
}
});
如您所见,value 的类型没有正确缩小:
考虑到GetEntryOf 可以做到这一点,我认为应该可以在mapperFn 内部做同样的事情。在当前的打字稿中这甚至可能吗?如果可以,那么合适的方法是什么?
注意:工作类型缩小示例基于this SO answer。我知道并且可以接受同一个答案中提到的约束:“不能保证该值也没有其他属性”。
【问题讨论】:
-
能否请您提供几个您期望的输出示例
-
所以根本问题是
value是string | number类型(在你的例子中),不管你缩小key吗?我怀疑你不能用mapObject的当前签名解决这个问题。如果您可以将其定义为接受一个元组而不是两个离散参数,我怀疑您可以。但这将涉及更改现有代码以接受元组而不是离散参数。 -
@t-j-crowder 确切地说,我希望
value类型从 if 分支中的string | number缩小,在 else 分支中导致类型never。作为一个相对新手的打字员,我不确定我是否理解“接受元组而不是离散参数”的含义,但如果我理解正确,你是说如果不更改 fn 签名就无法完成,因此它是如何实现的调用。我可以将签名从(key, value)更改为(entry)或任何其他不会过多改变整个函数调用方式的东西。你能提供一个元组调用的例子吗? -
@T.J.Crowder 我搞砸了 ping。有关答案/问题,请参见上文。 :)
-
(A tuple 基本上是一个固定长度的数组,其中元素有自己的类型。
[number, number]是一个元组。)我是说我不确定确定 i> 它可以在不切换到元组的情况下完成。 :-) 可悲的是,我在使用元组时也没有太多运气,但我觉得这是可能的。 @captain-yossarian 对 TS 的了解比我多得多,希望他能提供帮助。
标签: typescript object mapping