1.从entries说起
Object.entries()方法,可以进行对象的遍历,并将其中的键值对转换为数组
let test = { a: 1, b: 2 };
console.log(Object.entries(test));//[ [ 'a', 1 ], [ 'b', 2 ] ]
如果我们在遍历时取出其中的key,会发现默认为string类型
假如我们有两个对象,想遍历时将其中一个对象的value,赋值到另外一个对象上
let test = { a: 1, b: 2 };
type Result = { 'c': number };
let result: Result = { c: 3 };
for(let [key, value] of Object.entries(test)) {
result[key] = value;
}
会发现有问题,因为entries返回的key为string,和Result类型不符
如果我们as断言,会发现不报错了,但是Result类型对键限制的目的,并没有达到,ab可以为键。
这是因为ts发现类型断言,就不会类型推断,直接采用断言给出的类型,即认为ab符合Result。
2.方法重写
那么我们怎样在entries遍历时,仍可以对键进行限制呢?
考虑如下类型。
type Entries<T> = {
[K in keyof T]: [K, T[K]]
}[keyof T][]
这是一个数组,数组的每一项即为一个键值对组成的数组。
实际上我们可以,写成一个新的entries函数。
function entries<T extends object>(obj: T) {
return Object.entries(obj) as Array<[keyof T, T[keyof T]]>
}
通过entends限制Object.entries传入对象,然后取出键值对,组成数组。
然后用前面的例子实验一下。
let test = { a: 1, b: 2 };
type Result = { 'c': number };
let result: Result = { c: 3 };
function entries<T extends object>(obj: T) {
return Object.entries(obj) as Array<[keyof T, T[keyof T]]>
}
for(let [key, value] of entries(test)) {
result[key] = value;
}
可以发现,报错为a不在Result类型中,成功限制住了。
Object.keys同理。
function objectKeys<T extends object>(obj: T) {
return Object.keys(obj) as Array<keyof T>
}
3.pr中的应用
可以考虑在utils中进行函数导出。
export function objectEntries<T extends object>(obj: T) {
return Object.entries(obj) as Array<[keyof T, T[keyof T]]>
}
export function objectKeys<T extends object>(obj: T) {
return Object.keys(obj) as Array<keyof T>
}
VueUse中有类似做法。
这是我刚提交的一个pr,已经被merge。为了修复一个暂时用注释指令解决的ts报错。
附上本人github,欢迎follow:)