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

She*_*ter 7 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
    ;
Run Code Online (Sandbox Code Playgroud)

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

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

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>
    ;
Run Code Online (Sandbox Code Playgroud)

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

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
    ;
Run Code Online (Sandbox Code Playgroud)

是否有替代解决方案可以让我避免写出仅因密钥名称而异的众多重复接口?

查看错误消息的“唯一符号类型”部分,我不相信我可以创建基于Symbols的枚举。

相关问题

在 TypeScript 接口中使用字符串枚举值作为计算属性键似乎非常接近,但它是关于在接口中使用特定的字符串枚举值,它在较新的 TypeScript 版本中工作。

art*_*tem 5

您可以使用映射类型,而不是使用计算属性:

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' } }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

const foo = ({ kind, lhs, rhs }: { kind: Operation, lhs: ApiItem, rhs: ApiItem }): ApiItem =>
({ [kind]: { lhs, rhs } });
Run Code Online (Sandbox Code Playgroud)

错误说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;
}
Run Code Online (Sandbox Code Playgroud)