发布于 1 年前 ,更新于 1 年前 typescript

Typescript 获取对象属性的全路径作为类型

在使用 vue-i18n 时发现 t() 函数是有类型提示的,并且类型就是从 message 对象中推导出来的。于是我也尝试写一下这个类型推导。

假如有一个对象 message :

const message = {
  a: {
    b: {
      c: {
        d: '1'
      }
    },
    b1: '2'
  }
}

从该对象中获取如下类型:

type KeyPath = 'a.b.c.d' | 'a.b1'

首先想到的写法是:

type PickupKey<T extends Record<string, any>, K = keyof T> = K extends string ? K : never

type KeyPath = PickupKey<typeof message> // => “a”

这个时候只拿到了第一层的属性,所以应该还需要一个递归,递归的时候需要将属性通过 . 拼接在一起:

type PickupPathKey<
  T extends Record<string, any>,
  K extends string,
  M = keyof T
> = M extends string ? PickupPathKey<T[M], `${K}.${M}`> : K

type PickupKey<
  T extends Record<string, any>,
  K = keyof T
> = K extends string ? PickupPathKey<T[K], K> : never

type KeyPath = PickupKey<typeof message> // => “a.b.c.d” | "a.b1"

这样就实现了。但 PickupPathKeyPickupKey 很相似,是否可以将这两个合并成一个?可以试试先

type PickupPathKey<
  T extends Record<string ,any>,
  K extends string,
  M = keyof T
> = M extends string ? PickupPathKey<T[M], `${K}.${M}`> : K

type KeyPath = PickupPathKey<typeof message, ''>  // => “.a.b.c.d” | ".a.b1"

与预期的差不多,但前面都多了一个 . 。继续改造

type PickupPathKey<
  T extends Record<string ,any>,
  K extends (string | null) = null,
  M = keyof T
> = M extends string ? PickupPathKey<T[M], (K extends string ? `${K}.${M}` : M)> : K

type KeyPath = PickupPathKey<typeof message> // => “a.b.c.d” | "a.b1"

与预期一致。这里借助了 null 类型和 K extends string 来判断是否是第一层的属性。

© 2016 - 2023 BY 禾惠 粤ICP备20027042号