ts遍历对象时类型的优化,以及VueUse社区的1次pr

460 阅读2分钟

1.从entries说起

Object.entries()方法,可以进行对象的遍历,并将其中的键值对转换为数组

let test = { a: 1, b: 2 };
console.log(Object.entries(test));//[ [ 'a', 1 ], [ 'b', 2 ] ]

如果我们在遍历时取出其中的key,会发现默认为string类型

image.png

假如我们有两个对象,想遍历时将其中一个对象的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类型不符

image.png

如果我们as断言,会发现不报错了,但是Result类型对键限制的目的,并没有达到,ab可以为键。

这是因为ts发现类型断言,就不会类型推断,直接采用断言给出的类型,即认为ab符合Result。

image.png

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;
}

image.png

可以发现,报错为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中有类似做法。

github.com/vueuse/moti…

这是我刚提交的一个pr,已经被merge。为了修复一个暂时用注释指令解决的ts报错。

附上本人github,欢迎follow:)

github.com/heygsc