typescript - 避免对联合类型进行愚蠢的推断( Element 隐含地具有“任何”类型,因为类型的表达式......)
问题描述
所以我有一个以前问过的问题的变体,但不知何故,答案没有帮助。这是代码:
type ModelA = {
type: 'a'
method: 'foo' | 'bar'
}
type ModelB = {
type: 'b'
method: 'baz'
}
type Model = ModelA | ModelB
const dictionary = {
a: {
foo: () => 'foo',
bar: () => 'bar'
},
b: {
baz: () => 'baz'
}
}
function getMethod(m: Model): Function {
return dictionary[m.type][m.method] <--- error
}
Element implicitly has an 'any' type because expression of type '"foo" | "bar" | "baz"' can't be used to index type '{ foo: () => string; bar: () => string; } | { baz: () => string; }'.
Property 'foo' does not exist on type '{ foo: () => string; bar: () => string; } | { baz: () => string; }'.
如果我像这样使用一些愚蠢的类型检查:
function getMethod(m: Model) {
if (m.type === 'a') {
return dictionary[m.type][m.method]
}
return dictionary[m.type][m.method]
}
它之所以有效,是因为在两个分支中,TS 都知道获取该方法是安全的。
我知道这应该使用通用解决,但不知何故我无法弄清楚。
解决方案
为了输入它,您需要推断dictionary
每个对象的属性:
type ModelA = {
type: 'a'
method: 'foo' | 'bar'
}
type ModelB = {
type: 'b'
method: 'baz'
}
type Model = ModelA | ModelB
const dictionary = {
a: {
foo: () => 'foo',
bar: () => 42
},
b: {
baz: () => 'baz'
}
}
const withDict = <
Key extends string,
SubKey extends string,
Fn extends (...args: any[]) => any,
Dict extends Record<Key, Record<SubKey, Fn>>
>(dict: Dict) =>
<
Type extends keyof Dict,
Method extends keyof Dict[Type],
>({ type, method }: { type: Type, method: Method }) =>
dict[type][method]
const getMethod = withDict(dictionary)
// () => string
const result = getMethod({ type: 'a', method: 'foo' })
// () => number
const result2 = getMethod({ type: 'a', method: 'bar' })
// expected error
const result3 = getMethod({ type: 'a', method: 'z' })
您可能已经注意到,我使用柯里化来推断dictionary
.
在您的情况下,TypeScript 无法推断dictiopnary
,因为它被声明在函数范围之外。
您可以在我的博客中找到更多函数参数推断示例
更新
您也可以使用这种方法:
const withConfig = <Dictionary,>(config: Dictionary) =>
<Type extends keyof Dictionary,
Method extends keyof (Dictionary)[Type]
>(
type: Type,
method: Method
) => config[type][method]
const applyConfig = withConfig({
a: {
foo: () => 'foo',
bar: () => 42
},
b: {
baz: () => 'baz'
}
})
applyConfig('a', 'bar') // ok
applyConfig('a', 'baz') // expected error
推荐阅读
- javascript - 使用选择过滤全日历事件
- api - 调用另一个 API 的 API 的 HTTP 方法是什么?
- eclipse - 在我新安装的 Eclipse 中,缺少 3 个文件夹。Java 资源、JAX-WAS Web 服务和部署描述符
- python - 是否有任何脚本或代码可以使用程序打开 Android 应用程序?
- python - 如何在python数据表中填充空值?
- assembly - MASM 不会插入带有某些 CPU 和 FPU 指令组合的 x87 WAIT 前缀
- google-chrome - 谷歌浏览器显示问题
- mongodb - 如何从另一个 mongodb 集合中获取字段并将这些字段值作为数组添加到当前集合中?
- javascript - React-router URL,路由节点/快递和文件结构
- gdb - 如何设置 gdb-multiarch 以支持 PowerPC 架构?