javascript - 如何在 TypeScript 中以变量作为键返回对象接口?类型不可分配给类型错误
问题描述
我有以下类型和接口。
type ColVal = [{
col: string
}, {
val: string
}]
interface IEquals {
eq: ColVal,
}
interface INotEquals {
ne: ColVal,
}
我有以下功能:
const getOperation = (col: string, val: string, operation: string): IEquals | INotEquals => {
let op: 'eq' | 'ne' = operation === 'Equals' ? 'eq' : 'ne';
return {
[op]: [{
col,
}, {
val,
}]
};
};
但我得到了错误Type '{ [x: string]: ({ col: string } | { val: string; })[]; }' is not assignable to 'IEquals | INotEquals'.
如果我更改[op]
为['eq']
or ['ne']
,错误就会消失。有人知道如何解决这个问题吗?
这是 TypeScript 游乐场,供大家查看问题:Playground。
解决方案
目前,TypeScript 的一个设计限制是联合类型的计算属性的键一直string
被编译器加宽。因此,编译器将类似的对象字面{[Math.random()<0.5 ? "a" : "b"]: 123}
量推断为类型{[k: string]:number}
而不是更具体的{a: number} | {b: number}
.
microsoft/TypeScript#13948和microsoft /TypeScript #21030 都涉及此问题。似乎有一次尝试解决它,microsoft/TypeScript#21070)但它失败了。
我不知道它是否会得到解决,但现在你必须解决它。
破坏性最小(并且类型安全性最低)的解决方法就是断言返回值是适当的类型。在这种情况下,编译器看到返回值的类型非常广泛,以至于它甚至不认为它与IEquals | INotEquals
. 所以你必须通过一些中间类型来断言......不妨只使用any
,最终的“让它工作”类型:
const getOperationAssert = (col: string, val: string, operation: string): IEquals | INotEquals => {
let op: 'eq' | 'ne' = operation === 'Equals' ? 'eq' : 'ne';
return {
[op]: [{
col,
}, {
val,
}]
} as any; // I'm smarter than the compiler
};
另一个想法是手动实现一个辅助函数,该函数的行为方式与计算属性“应该”的行为方式相同。像这样:
function computedProp<K extends PropertyKey, V>(key: K, val: V): { [P in K]: { [Q in P]: V } }[K];
function computedProp(key: PropertyKey, val: any) {
return { [key]: val };
}
因此,如果您调用computedProp(Math.random()<0.5 ? "a" : "b", 123)
,该实现只会创建一个具有计算属性的对象,但类型是这样的,它会返回{a: number} | {b: number}
。然后你的getOperation()
变成:
const getOperationHelper = (col: string, val: string, operation: string): IEquals | INotEquals => {
let op: 'eq' | 'ne' = operation === 'Equals' ? 'eq' : 'ne';
return computedProp(op, [{
col,
}, {
val,
}]);
};
最后,如果你愿意重构,你可以考虑完全不使用计算属性。将您的ColVal
值存储在一个变量中,然后将其作为带有文字键的对象文字eq
或带有文字键的对象文字的属性返回ne
。编译器可以更准确地遵循该流程,并可以验证它是否安全:
const getOperationRefactor = (col: string, val: string, operation: string): IEquals | INotEquals => {
let op: 'eq' | 'ne' = operation === 'Equals' ? 'eq' : 'ne';
const colVal: ColVal = [{ col }, { val }];
return (operation === 'Equals') ? { eq: colVal } : { ne: colVal };
};
希望其中之一对您有用。
推荐阅读
- node.js - NodeJS集群背后的算法是什么?
- amazon-web-services - 可以使用角色使用 AWS SAM 进行本地开发吗?
- c# - 等待外部 api 方法完成
- validation - Asp.Net Core 2 MVC 部分视图中未显示验证错误消息
- c# - 是否有针对 c# 语言版本的指令
- c# - 引用 Windows 运行时组件时 Unity 应用程序通过 Windows 应用程序认证工具包测试的问题
- javascript - 关闭 MUI Snackbar 通知并在按键上执行操作
- azure - 设置新发布管道时未显示 Azure DevOps 服务连接
- primefaces - 数据表 - 上下文菜单
- apache-kafka - 用于 Kafka 分布式连接器的 Vault