typescript - 在 Typescript 中推断深度嵌套对象的非叶节点类型
问题描述
我一直在玩Inferring Typescript 中深度嵌套对象的类型。
原始代码
const theme = {
button: { margin: { sm: "sm" } },
form: { padding: { sm: "sm1" } }
} as const;
type Theme = typeof theme;
const getStyle = <
K extends keyof Theme,
S extends keyof Theme[K],
M extends keyof Theme[K][S]
>(t: Theme, name: K, style: S, mod: M) => t[name][style][mod];
getStyle(theme, 'button', 'margin', 'sm');
我从原始示例中引入了一种变体 - 在我的代码中叶节点始终具有相同的结构{sm: string}
。
我正在努力以getStyle
这样一种方式进行修改,即客户端只能指定 2 级密钥,并受益于节点结构始终相同的事实;
const getStyleNew = <
K extends keyof Theme,
S extends keyof Theme[K]
>(t: Theme, name: K, style: S) => t[name][style].sm;
不幸的是,这失败了:
Property 'sm' does not exist on type '{ readonly button: { readonly margin: { readonly sm: "sm"; }; }; readonly form: { readonly padding: { readonly sm: "sm"; }; }; }[K][S]'.
有没有办法让编译器相信 smt[name][style]
在修改后的函数中可用?
解决方案
一种方法是将样式记录分配给具有sm
使用条件Index
类型键入的属性的对象,以说明可能缺少sm
. 这甚至会为结果推断出确切的文字类型,而不仅仅是string
. 这是一个通用解决方案,但您也可以使用Theme
代替T
:
type Index<T, K> = K extends keyof T ? T[K] : undefined;
const getStyleNew = <
T,
K extends keyof T,
S extends keyof T[K]
>(t: T, name: K, style: S) => {
const {sm}: {sm: Index<T[K][S], 'sm'>} = t[name][style]
return sm
};
const test1 = getStyleNew(theme, 'button', 'margin'); // inferred: test1: "sm"
const test2 = getStyleNew(theme, 'form', 'padding'); // inferred: test2: "sm1"
如果您查询没有sm
(例如header: { fontSize: {} }
)的样式记录,则推断的类型将是undefined
:
const test3 = getStyleNew(theme, 'header', 'fontSize'); // inferred: test3: undefined
不确定这是否是最优雅的解决方案,但extends
条款保证键的类型安全,所以它应该是正确的。
推荐阅读
- angularjs - 如何在 AngularJS 中复制richTextField.document.execCommand
- selenium - 从服务器失败的无头浏览器在本地工作
- reactjs - 为什么从组件中提取钩子时的工作方式不同?
- python - 添加“从文件读取”功能
- cron - 如何编辑之前发送的嵌入?
- python - 计算列表中具有相同值的子列表
- macos - 如何在 Mac 上将屏幕亮度提高到超过限制
- google-apps-script - 将 Google Drive 目录中的所有文件还原为旧版本
- spring - 无法使用 API Gateway Spring Cloud 调用实际服务
- python - Python 将方法应用于两个大列表中的元素对需要很长时间来处理