typescript - 如何交换对象键值并获得交换函数的有效返回类型?
问题描述
当我交换对象键值时,函数的返回类型是 { [x: string]: value } instedof const key。
这里的游乐场
interface IObj {
[key: string]: string;
};
const swapFn = <T extends IObj>(obj: T) => {
const res = {} as { [K in T[keyof T]]: keyof T };
Object.entries(obj).forEach(([key, value]) => {
res[value as T[keyof T]] = key;
});
return res;
}
// return type should be:
// {
// aaa: 'a',
// bbb: 'b',
// } insted of {
// [x: string]: "a" | "b";
// }
const result = swapFn({
a: 'aaa',
b: 'bbb',
});
解决方案
这里有两个问题。首先是您的返回类型太宽,因为keyof T
是所有键的联合;每个属性都将是"a" | "b"
而不是特定的"a"
或"b"
对应于该值。这可以通过更改映射类型来解决,并且可以通过TypeScript 4.1 中引入的键重新映射特别容易地完成:
{ [K in keyof T as T[K]]: K }
我们遍历 的每个键K
,T
并将输出T[K]
(属性值类型)作为键,并K
作为属性。这交换了东西。
第二个问题是推理问题。当您传入编译器时{ a: 'aaa', b: 'bbb'}
,编译器很可能会将该值推断为具有类型{a: string, b: string}
,因为人们可能希望将属性的值更改为其他值string
而不是将其保留为某个字符串文字更常见。有不同的方法可以说服编译器推断出更窄的类型。最简单的是调用者使用const
断言:
swapFn({ s: "t", u: "v" } as const);
但是,如果您不希望调用者必须这样做,您可以使用其他技巧。请参阅microsoft/TypeScript#30680以获取功能请求,要求提供一些不那么疯狂的技巧和对它们的一些描述。我将添加另一个泛型类型参数S extends string
,除了向编译器提示您需要字符串文字类型之外,它没有其他用途。代替T extends IObj
,我会写T extends Record<string, S>
。
这是完整的功能:
const swapFn = <T extends Record<string, S>, S extends string>(obj: T) => {
const res = {} as any; // I'm not worried about impl safety
Object.entries(obj).forEach(([key, value]) => {
res[value] = key;
});
return res as { [K in keyof T as T[K]]: K };
}
我改变了类型断言;编译器并没有真正具备验证“交换键和值”实现的类型安全性,所以我不想费心去尝试,而是使用any
.
让我们看看它是否有效:
const result = swapFn({
a: 'aaa',
b: 'bbb',
});
/* const result: {
aaa: "a";
bbb: "b";
} */
console.log(result.aaa.toUpperCase()) // A
看起来挺好的!
推荐阅读
- html - 如何使用 nth-child inline 更改行的背景颜色?
- javascript - 如何检查电子邮件是否在 Firebase 中验证?
- powershell - 如何开始?
- r - 直方图未显示在 R 中?特别是查看器平移
- javascript - 从 sapper 预加载初始化可写的 svelte 存储
- swift - 升级 Swift 编译器
- php - 在 laravel 雄辩的查询中小写
- maven - 使用 schema-registry 时遇到问题:下载
- winapi - 如何检索 AP 可以接受的所有身份验证/密码对的完整列表?
- node.js - 无法使用 import 而不是 require,或者无法使用 ts-node 和 tsdx 构建依赖项