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

在TypeScript中使用转换映射重命名对象的属性?

  •  1
  • dwjohnston  · 技术社区  · 1 年前

    我正在努力实现这样的目标:

    const input = {a: 1, b: "hello"}; 
    const result = renameProperties(input,{a: "alpha", b: "beta"}); 
    result.alpha // number
    result.beta // string
    result.foo //Property 'foo' does not exist on type... 
    
    

    所以我的转换函数是这样的:

    
    export function renameProperties<
      TInput extends Record<string, unknown>,
      TConversionMap extends Record<keyof TInput, string>,
      TOutput extends // heres where I get stuck 
    >(input: TInput, conversionMap: TConversionMap): TOutput {
    
    }
    

    作为第一步,我有:

    TOutput extends {[K in TConversionMap[keyof TInput]] : string}
    

    这并不是我想要的行为——我希望结果只包含转换后的密钥。

    result.alpha // string
    result.beta // string
    
    result.foo //string  - but really was hoping to see an error here 
    
    

    第二部分将涉及保留输入的值,我想的是:

    TOutput extends {[K in TConversionMap[keyof TInput]] : TInput[The Input key, if we can retain a reference to it]}
    

    我该怎么做?这可能吗?

    1 回复  |  直到 1 年前
        1
  •  1
  •   jcalz    1 年前

    你最想使用 key remapping in mapped types 你的 renameProperties() 函数应该是 generic 在里面 T ,输入的类型,以及 M ,键映射的类型。然后对于每个键 K 在里面 keyof T ,您使用键创建属性 M[K] :

    declare function renameProperties<
      T extends object,
      const M extends Record<keyof T, PropertyKey>
    >(input: T, mapping: M):
      { [K in keyof T as M[K]]: T[K] }
    

    在这里你可以看到 M 已经 constrained 与具有相同密钥的东西 T 并且其属性本身都是类似密钥的( PropertyKey ).

    这给了你一切技术上需要的工作。


    唯一的问题是,如果将映射对象写为 object literal 对于映射,TypeScript默认情况下不会跟踪 literal types 的属性值。如果你写 {a: "x", b: "y"} ,TypeScript将推断为 {a: string, b: string} ,这对你没有帮助。你会想要一个 const assertion 如在 {a: "x", b: "y"} as const 相反

    一个改进是 M a const type parameter ,以便如果映射参数是对象文字,则TypeScript将其视为 const 断言存在,因此调用者可以忽略它:

    const input = { a: 1, b: "hello" };
    const result = renameProperties(input, { a: "alpha", b: "beta" });
    //    ^? const result: { alpha: number; beta: string; }
    

    因此,这符合要求。请注意,如果映射为 内联传递,那么您需要 const 断言。没有什么能神奇地让它为你工作:

    const mapping = { a: "x", b: "y" };
    //    ^? const mapping: {a: string, b: string}
    const r2 = renameProperties(input, mapping);
    //    ^? const r2: { [k: string]: string | number } 
    

    所以你会想要

    const mapping = { a: "x", b: "y" } as const;
    const r2 = renameProperties(input, mapping);
    //    ^? const r2: { x: number; y: string }
    

    Playground link to code

    推荐文章