繁体   English   中英

如何在TypeScript中以变量作为键返回对象接口? 类型不可分配给类型错误

[英]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#13948microsoft/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 };
};

希望其中之一对您有用。

Playground 链接到代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM