Typescript类型体操:获取对象嵌套属性🔥🔥

1,642 阅读1分钟

怎么获取一个object中所有的属性

描述

举例:

type Person = {
  name: {
    fullname: string;
    nickname: string;
  };
  age: number;
  job: {
    name: string;
    address: {
      country: string;
      detail: string;
    };
  };
}


function toPath<T>(obj: T, path: string) { 
  console.log(obj, path);
}

const person: Person = {
  name: {
    fullname: '',
    nickname: '',
  },
  age: 30,
  job: {
    name: '',
    address: {
      country: '',
      detail: '',
    },
  },
};

/** 调用时,自动枚举出以下属性: **/
// name
// name.fullname
// name.nickname
// age
// job
// job.name
// job.address
// job.address.country
// job.address.detail

toPath(person, 'name.fullname');
toPath(person, 'job.address.country');

实际应用场景:

lodash中的toPath函数

WX20220928-192953@2x.png

问题

显然,由于pathstring类型,这里并没有类型约束,填啥也不会报错!! 对于程序运行时来说,相当不友好

分布实现

1. 首先获取第一层属性

对大多数同学来说,都非常简单,直接使用keyof即可,如下:

在线尝试

type Path = keyof Person;

function toPath<T>(obj: T, path: Path) { 
  console.log(obj, path);
}

toPath(person, 'name');

2. 实现嵌套获取

  • 声明一个新类型
type NestedPath<ObjectType extends object> = {
  // todo
}[keyof ObjectType];

type Paths = NestedPath<Person>;
  • 遍历对象的属性
type NestedPath<ObjectType extends object> = {
  [Key in keyof ObjectType]: ObjectType[Key]   
}[keyof ObjectType];

显然,这样只能获取一层。 当值为object,需要嵌套调用NestedPath

  • 嵌套调用NestedPath
type NestedPath<ObjectType extends object> = {
  [Key in keyof ObjectType]:
    ObjectType[Key] extends object
      ? `${Key}.${NestedKeyOf<ObjectType[Key]>}`
      : Key
}[keyof ObjectType];

此时路径并不包含name, job, 并且Key存在报错

在线尝试

3.png 2.png

添加属性本身 & 添加Key类型约束

在线尝试

type NestedPath<ObjectType extends object> = 
  {
    [Key in keyof ObjectType & (string | number)]:
      ObjectType[Key] extends object
        ? Key | `${Key}.${NestedPath<ObjectType[Key]>}`
        : `${Key}`
          
  }[keyof ObjectType & (string | number)];

到此,实现结束~

思考?????

若job是个数组类型,该如何调整??需求如下:

type Person = {
  name: {
    fullname: string;
    nickname: string;
  };
  age: number;
  job: {
    name: string;
    address: {
      country: string;
      detail: string;
    };
  }[];
}

const person: Person = {
  name: {
    fullname: '',
    nickname: '',
  },
  age: 30,
  job: [
    {
      name: 'tom',
      address: {
        country: '',
        detail: '',
      },
    }
  ],
};

答案戳👇