首页 > 解决方案 > 连接字符串作为打字稿中对象的键?

问题描述

假设我有以下 JS 代码:

export const giveMeFunctions = (namePrefix, number) => {
    const functionA = (x) => number + x;
    const functionB = (x, y) => number * x - y;
    
    return {
        [`${namePrefix}Add`]: functionA,
        [`${namePrefix}MultSub`]: functionB,
    };
}

// This is handy because I can then do:
const {piAdd, piMultSub} = giveMeFunctions('pi', Math.PI);
const {eAdd, eMultSub} = giveMeFunctions('e', Math.E);
// No confusion between variable names, and object interpolation makes it easy to use

但是,我想不出一种在 Typescript 中键入此函数的返回值的好方法。正如你所看到的,这两个键有不同的类型,所以我不能简单地做类似的事情

const giveMeFunctions<N extends string> = (number: number): {
    [key: string]: Function;
} => {...}

因为这将允许意外交换 和 的functionA参数functionB。在我看来,这应该可以通过 Typescript 实现,因为这在编译时都是可能的。例如,如果我们简单地取出连接步骤,这将很容易实现:

export const giveMeFunctions<T extends string> = (number: number): Record<T, string> => {...}

我期望的工作将是:

const giveMeFunctions<N extends string> = (number: number): {
    [N + 'Add']: string,
    [N + 'MultSub']: string,
} => {...}

甚至:

const giveMeFunctions<N extends string> = (namePrefix: N, number: number): {
    [N + 'Add']: string,
    [N + 'MultSub']: string,
} => {...}

但是这两个都抱怨:

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

有什么方法可以通过用户指定的字符串和硬编码后缀的串联定义的键来实现对象类型的预期效果?

标签: typescriptobjecttypes

解决方案


你可以这样做:

const record = <
  Prop extends PropertyKey,
  Value extends (...args: any[]) => any
>(prop: Prop, value: Value) =>
  ({ [prop]: value }) as Record<Prop, Value>

export const giveMeFunctions = <
  T extends string,
  N extends number
>(namePrefix: T, number: N) =>
({
  ...record(`${namePrefix}Add`, (x: N) => number + x,),
  ...record(`${namePrefix}MultSub`, (x: N, y: N) => number * x - y),
})

const result = giveMeFunctions('pi', Math.PI);
result.piAdd // (x: number) => number
result.piMultSub // (x: number, y: number) => number

操场

record功能可以解决问题。默认情况下,具有类似计算属性的对象{[prop]:key}被解析{[props:string]:any type}

您可能已经注意到,我使用类型转换 as Record<Prop, Value>来避免这种行为


推荐阅读