代码之家  ›  专栏  ›  技术社区  ›  DanielGibbs

如何在TypeScript函数的返回类型中将参数的值用作属性名?

  •  0
  • DanielGibbs  · 技术社区  · 4 年前

    const objectToArray = <T>(object: { [id: string]: T }): (T & { id: string })[] => {
      return Object.keys(object).map((key) => ({ id: key, ...object[key] }))
    }
    

    这将转换 { a: { value: 1 }, b: { value: 2 } } 进入之内 [{ id: 'a', value: 1 }, { id: 'b', value: 2 }] .

    我想做的是取第二个参数 idField 它是要用作返回对象中ID字段的对象的字段的名称。E、 g.如果 艾德菲尔德 有价值吗 userId 那么结果就是 [{ userId: 'a', value: 1 }, { userId: 'b', value: 2 }]

    我已经在函数的逻辑中完成了这一点,但是我不知道返回类型应该是什么:

    export const objectToArray = <T>(object: { [id: string]: T }, idField: string): ???[] => {
      return Object.keys(object).map((key) => ({ [idField]: key, ...object[key] }))
    }
    

    我试着把返回类型 (T & { [idField]: string })[] 但我得到了错误 A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. .

    1 回复  |  直到 4 年前
        1
  •  1
  •   Chase    4 年前

    这有点复杂,因为 价值观 (函数参数)在运行时 类型 只有编译时。没有什么能阻止你通过考试 非常数 字符串到 idField

    然而,你 可以 得到你想要的类型安全 如果将常量字符串传递给函数 .

    让我们从一个基本示例开始(在javascript省略类型中)-

    function foo(prop) {
        return {
            [prop]: 42
        };
    }
    

    function foo<T extends string>(prop: T): Record<T, number> {
        return {
            [prop]: 42
        } as Record<T, number>;
    }
    

    试穿一下 playground

    这是怎么回事?我们先确定 prop 是一个扩展字符串的类型(也可以是常规的 T extends string | number | symbol )-因为这是你需要使用的类型 道具 作为一个 财产 .

    然后我们使用这个类型来创建返回类型 Record<T, number> . 这和-

    {
        [k in T]: number
    }
    

    您可以使用或。

    然而,这种断言是必要的。因为typescript不能强制参数始终是编译时常量。

    let s = foo('userName');
     // ^ Record<'userName', number>
    
    s.userName; // all good
    s.otherProp; // Property 'otherProp' does not exist on type 'Record<"userName", number>'
    

    好的,现在我们用这个函数-

    export const objectToArray = <T, K extends string>(
        object: { [id: string]: T },
        idField: K
    ): (T & Record<K, string>)[] => {
        return Object.keys(object).map(key => ({ [idField]: key, ...object[key] })) as (T & Record<K, string>)[];
    };
    

    注意,我假设 K (类型) 艾德菲尔德 )又是一根弦,你可以用 K extends string | number | symbol 取决于你的情况。我还假设房产的价值 K公司 string ,因此 Record<K, string>

    试穿一下 playground