如何在接口中使用字符串枚举类型作为计算属性名称?

时间:2018-06-14 03:54:36

标签: typescript

我在TypeScript中建模API请求格式:

interface ApiTerm {
    term: {
        name: string,
        value: string,
    }
}

interface ApiAnd {
    and: {
        lhs: ApiItem,
        rhs: ApiItem,
    }
}

interface ApiOr {
    or: {
        lhs: ApiItem,
        rhs: ApiItem,
    }
}

type ApiItem =
    | ApiTerm
    | ApiAnd
    | ApiOr
    ;

这有效,但我需要实现许多二进制操作,而不仅仅是"和"和"或"所以我想要一些缩短和重用代码的方法。

在我编写的其他代码中,我在接口中使用了特定的字符串枚举 value ,我尝试使用字符串枚举作为属性名称:

enum Operation {
    And = "and",
    Or = "or",
}

interface ApiBinary<O extends Operation> {
    [O]: {
        lhs: ApiItem,
        rhs: ApiItem,
    }
}

type ApiItem =
    | ApiTerm
    | ApiBinary<Operation.And>
    | ApiBinary<Operation.Or>
    ;

不幸的是,这会在TypeScript 2.9.1中生成错误:

A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.

是否有另一种解决方案可以让我避免写出只会因密钥名称而有所不同的众多重复接口?

查看&#34; a&#39;独特符号&#39;类型&#34;部分错误消息,我不相信我可以根据Symbol创建枚举。

相关问题

Use string enum value in TypeScript interface as a computed property key似乎非常接近,但是关于在接口中使用特定的字符串枚举值,它在较新的TypeScript版本中以原样方式工作。

1 个答案:

答案 0 :(得分:3)

您可以使用映射类型:

,而不是使用计算属性
interface ApiTerm {
    term: {
        name: string,
        value: string,
    }
}

enum Operation {
    And = "and",
    Or = "or",
}

type ApiBinary<O extends Operation> = {[o in O]: {
        lhs: ApiItem,
        rhs: ApiItem,
    }
}

type ApiItem =
    | ApiTerm
    | ApiBinary<Operation.And>
    | ApiBinary<Operation.Or>
    ;

const andExample: ApiBinary<Operation.And> = {
    'and': {
        lhs: { term: { name: 'a', value: 'b' } },
        rhs: { term: { name: 'c', value: 'd' } }
    }
}

但是,请注意,没有办法表达ApiBinary只能拥有一个属性的限制;例如,有人可以声明type N = ApiBinary<Operation.And | Operation.Or>;

当您使用映射类型时,它与计算属性不能很好地混合 - 编译器无法推断计算属性类型符合此示例中ApiItem中的约束:

const foo = ({ kind, lhs, rhs }: { kind: Operation, lhs: ApiItem, rhs: ApiItem }): ApiItem =>
({ [kind]: { lhs, rhs } });

错误显示is not assignable to type 'ApiBinary<Operation.Or>',因为编译器会尝试检查所有联合成员的可分配性,如果失败则只会告诉最后一个。

我能想到写这个函数的唯一方法是啰嗦:

const foo = <O extends Operation>({ kind, lhs, rhs }: { kind: O, lhs: ApiItem, rhs: ApiItem }) => {
    const result = {} as ApiBinary<O>;
    result[kind] = {lhs, rhs};
    return result;
}