[英]How to return object interface with variable as a key in TypeScript? Type is not assignable to type error
我有以下类型和接口。
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'.
Type '{ [x: string]: ({ col: string } | { val: string; })[]; }' is not assignable to 'IEquals | INotEquals'.
如果我将[op]
更改为['eq']
或['ne']
,错误就会消失。 有谁知道如何解决这个问题?
这里是 TypeScript 游乐场,供你们查看问题: Playground 。
目前 TypeScript 的一个设计限制是联合类型的计算属性的键被编译器一直扩展到string
。 所以像{[Math.random()<0.5 ? "a" : "b"]: 123}
这样的对象字面量{[Math.random()<0.5 ? "a" : "b"]: 123}
{[Math.random()<0.5 ? "a" : "b"]: 123}
被编译器推断为{[k: string]:number}
而不是更具体的{a: number} | {b: number}
{a: number} | {b: number}
。
microsoft/TypeScript#13948和microsoft/TypeScript#21030 都关注这个问题。 看起来曾经有人试图解决它, microsoft/TypeScript#21070 ) 但它失败了。
我不知道它是否会得到解决,但现在你必须解决它。
破坏性最小(且类型最不安全)的解决方法是断言返回值是适当的类型。 在这种情况下,编译器看到返回值的类型太宽,以至于它甚至不认为它与IEquals | INotEquals
相关IEquals | INotEquals
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}
{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 };
};
希望其中之一对您有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.