如何基于对象数组属性值声明类型?含嵌套可选场景
如何从带可选嵌套的数组中推断所有层级的key联合类型?
在普通非嵌套、无可选属性的场景下,我们可以通过as const断言保留数组元素的字面量类型,再提取key的联合类型:
const data = [ { key: 'name', desc: 'desc...' }, { key: 'password', desc: 'desc' }, { key: 'balabala', desc: 'bb.' } ] as const; type DataKey = typeof data[number]['key']; // "name" | "password" | "balabala" const fn = (key: DataKey) => { switch (key) { case 'name': break; // 其他case会自动提示可选的key值 } };
但遇到带可选嵌套属性的结构时,直接用宽泛类型注解会丢失字面量信息,导致无法提取所有层级的key。下面是解决方法:
步骤1:保留字面量类型
首先不要给数组指定Deep[]这类宽泛类型,改用as const断言让TypeScript保留所有具体的字面量细节,包括嵌套结构和可选属性的实际值:
const dataDeep = [ { key: 'name', desc: 'desc...' }, { key: 'password', desc: 'desc' }, { key: 'balabala', desc: 'bb.', child: [{ key: 'deep', desc: 'deep' }] } ] as const;
步骤2:编写递归类型工具
通过递归类型遍历所有层级的结构,提取每个层级的key并合并成联合类型:
// 递归提取所有层级的key类型 type ExtractAllKeys<T> = // 如果是数组,先推断数组元素类型 T extends readonly (infer Item)[] ? Item extends { key: infer Key; child?: infer Children } // 提取当前层级的key,再递归处理子级 ? Key | ExtractAllKeys<Children> : never : never; // 得到所有层级的key联合类型:"name" | "password" | "balabala" | "deep" type AllDeepKeys = ExtractAllKeys<typeof dataDeep>;
验证效果
使用这个联合类型作为函数参数类型,TypeScript会自动提示所有合法的key值,且不允许传入未定义的key:
const deepFn = (key: AllDeepKeys) => { switch (key) { case 'name': break; case 'password': break; case 'balabala': break; case 'deep': break; // 传入其他值会触发类型错误 } };
关键说明
- 为什么原来的写法失效?因为给
dataDeep指定Deep[]类型后,TypeScript会把具体的key字面量(如'name')拓宽为string,丢失了精确的类型信息。 - 递归类型会自动处理可选的
child属性:如果元素没有child,Children会是undefined,此时ExtractAllKeys<undefined>返回never,不会影响最终的联合类型。
内容的提问来源于stack exchange,提问作者taotao
相关产品推荐
相关产品推荐

