首页 > 解决方案 > 泛型类型不解析为联合

问题描述

以下代码错误:


class Base { }

class Child1 extends Base {
  child1Fn() {}
  static deserialize(bytes: Uint8Array): Child1 {
    return new Child1();
  }
}

class Child2 extends Base {
  child2Fn() {}
  static deserialize(bytes: Uint8Array): Child2 {
    return new Child2();
  }
}

const childMap = {
  "child1": Child1.deserialize,
  "child2": Child2.deserialize
}

function deserialize<T>(
  data: { name: string, bytes: Uint8Array },
  deserializeMap: Record<string, (bytes: Uint8Array) => T>
): T {
  const deserializeFn = deserializeMap[data.name];
  if (deserializeFn) {
    return deserializeFn(data.bytes)
  }
}

function deserializeChildMap(data: { name: string, bytes: Uint8Array }) {
  return deserialize(data, childMap)
}

游乐场链接

我最终得到了错误:

Argument of type '{ "child1": (bytes: Uint8Array) => Child1; "child2": (bytes: Uint8Array) => Child2; }' is not assignable to parameter of type 'Record<string, (bytes: Uint8Array) => Child1>'.
  Property '"child2"' is incompatible with index signature.
    Type '(bytes: Uint8Array) => Child2' is not assignable to type '(bytes: Uint8Array) => Child1'.
      Property 'child1Fn' is missing in type 'Child2' but required in type 'Child1'.

它看起来将类型解析为TchildMap (Child1) 中的第一个返回值。理想情况下,我想T解决Child1 | Child2. 有没有办法做到这一点?

标签: typescript

解决方案


可以做到,但需要更多的泛型类型参数:

class Base { }

class Child1 extends Base {
  child1Fn() { }
  static deserialize(bytes: Uint8Array): Child1 {
    return new Child1();
  }
}

class Child2 extends Base {
  child2Fn() { }
  static deserialize(bytes: Uint8Array): Child2 {
    return new Child2();
  }
}

const childMap = {
  "child1": Child1.deserialize,
  "child2": Child2.deserialize
}

function deserialize<TName extends string, TMap extends Record<TName, (bytes: Uint8Array) => any>>(
  data: { name: TName, bytes: Uint8Array },
  deserializeMap: TMap
): ReturnType<TMap[TName]> {
  const deserializeFn = deserializeMap[data.name];
  if (deserializeFn) {
    return deserializeFn(data.bytes)
  }
}

function deserializeChildMap<TName extends keyof typeof childMap>(data: { name: TName, bytes: Uint8Array }): ReturnType<typeof childMap[TName]> {
  return deserialize(data, childMap)
}

let c1 = deserializeChildMap({ name: "child1", bytes: null!}) // child1 
let c2 = deserializeChildMap({ name: "child2", bytes: null!}) // child2 

推荐阅读