在typescript中创建对象映射函数

时间:2023-12-09 阅读:59 评论:0 作者:yc888

<AK extends string, DK extends string>(map: Record<AK, DK>)

这不起作用,因为您丢失了键和值之间的映射。这意味着如果您有一个映射对象:

{ a: 'x', b: 'y' }

你最终会得到:

{ [K in 'a' | 'b']: 'x' | 'y' }

现在该类型无法再跟踪'a'映射到'x''b'映射到 的类型'y'

您还有一个返回类型,Record<any, GV>它没有任何帮助,因为键实际上是any

所以我们需要一个更好的输入泛型类型和一个更好的输出泛型类型。


要解决此问题,您需要对整个对象类型(键映射和数据)进行通用,而不仅仅是它们的键和值。

export function createMapper<  const TKeyMap extends Record<string, string>,>

现在您可以通过 获取键keyof TKeyMap或通过 获取值TKeyMap[keyof TKeyMap]或者,更重要的是,将类型映射为:

{ [K in keyof TKeyMap]: TKeyMap[K] } // identical to `TKeyMap`

此映射类型保留哪些键映射到哪些值。这个非常重要。

最后,const泛型类型的前缀告诉 Typescript 推断文字类型,而不仅仅是string这将推断TKeyMap为 as{ a: 'x', b: 'y' }而不是{ a: string, b: string }但你也可以as const在传入的对象后面抛出,如下所示:

createMapper({ ... } as const)

现在可以更简单地输入内部函数参数和泛型:

  return <    TData extends Partial<      Record<keyof TKeyMap, unknown>
    >
  >(
    objectToMap: TData
  ): // TODO: return type here

这表示该objectToMap参数必须具有与 相同的键的子集TKeyMap,每个键都有一个unknown值类型。TData将捕获任何东西,我们可以通过它。


内部函数的返回类型是有趣的地方。看起来像这样:

  {
    [ K in keyof TData as K extends keyof TKeyMap
      ? TKeyMap[K]
      : never
    ]: TData[K]
  }

这就是映射类型的用武之地。

它映射 的键TData并根据 重命名TKeyMap它们as

但我们需要确保它K实际上位于TKeyMap第一个,因为TData可能是具有额外键的约束的子类型。如果密钥在那里,则查找将其映射为新密钥名称的内容。如果不存在,则该键解析为never并从结果中省略该键。

对于值,只需使用TData.key上的任何值类型K即可TData[K]


对于函数体,可以使用以下方法来避免一些头痛:

const result = {} as any

然后进行您的实施。我几乎从不建议使用any,但在像这样的高度通用的情况下,一个简单的函数具有复杂的输入/输出类型,很难让打字稿来允许你尝试做的事情。

因此,用一些测试来覆盖这个函数,并告诉 Typescript 你知道你在做什么。


把所有这些放在一起可能看起来像:

export function createMapper<  const TKeyMap extends Record<string, string>,>(map: TKeyMap) {  return <    TData extends Partial<      Record<keyof TKeyMap, unknown>
    >
  >(
    objectToMap: TData
  ): {
    [ K in keyof TData as K extends keyof TKeyMap
      ? TKeyMap[K]
      : never
    ]: TData[K]
  } => {
    // Typescript's going to have a hard time here. So I would
    // type this as any, do the things you need to do and cover
    // this with some tests.
    const result = {} as any 
    for (const [key, value] of Object.entries(objectToMap)) {      const mappedKey = map[key]      if (mappedKey) result[mappedKey] = value
    }
    return result
  }}

其效果如您所料:

const mapMySchema = createMapper({  a: 'XX',  b: 'YY',  c: 'ZZ',})const body = mapMySchema({  a: 'aee',  b: 123,})// type:  { XX: string, YY: number }// value: { XX: 'aee', YY: 123 }


本文链接: http://a.10zhan.com/post/4297.html 转载请注明出处!