首页 > 解决方案 > 在 Typescript 中强制泛型类型参数满足基于函数重载定义的类型约束

问题描述

下面给出了简化问题的必要代码框架。基本上,可以有不同类型的Source对象。为简单起见,以下代码中显示了两种类型的源:DirectSourceIndirectSource. 该函数sourceAdapter()接受Source和其他辅助参数。

在示例中:如果key缺少参数,则Source必须是DirectSource; ifkey是单个字符串值 thenSource必须是IndirectSource,并且在这两种情况下,代码都会执行必要的调整并返回一个DirectSource对象。如果key是一个数组,Source则应该是IndirectSource它也是函数的返回值。


type KeyMap<T> = { [key in keyof T]: number };

type DirectSource = {
  value: number;
  otherFieldsAndMethods: any;
};

type IndirectSource<T extends object> = {
  kvMap: KeyMap<T>;
  otherFieldsAndMethods: any;
};

type Source<T extends number | object> = T extends object ? IndirectSource<T> : DirectSource;

// overloads
function sourceAdapter(src: Source<number>): DirectSource;
function sourceAdapter<T extends object>(src: Source<T>, key: keyof T): DirectSource;
function sourceAdapter<T extends object>(src: Source<T>, key: (keyof T)[]): IndirectSource<T>;

function sourceAdapter<T extends number | object>(
  src: Source<T>,
  key?: keyof T | (keyof T)[]
): T extends object ? IndirectSource<T> : DirectSource {

  if (key) { // According to function overloads Source must be an IndirectSource
    if (key instanceof Array) { // Config and return IndirectSource
      const kvMap = key.reduce((ac, s) => {
        ac[s] = (src as any).kvMap[s];
        return ac;
      }, {} as any);

      // ******Error here:
      // Type 'T' does not satisfy the constraint 'object'.
      let ret: IndirectSource<T> = {
        kvMap,
        otherFieldsAndMethods: src.otherFieldsAndMethods
      };
      return ret;
    } else { // Config and return DirectSource
      let directSource = {
        otherFieldsAndMethods: src.otherFieldsAndMethods,
        value: (src as IndirectSource<any>).kvMap[key],
      };
      return directSource; // ******Error here: assignability
    }
  } else { // Source is a DirectSource, simply return the src.
    return src;
  }
}

产生错误的行用星号标记。这是游乐场的链接。

标签: javascripttypescriptvisual-studio-codetypescript-generics

解决方案


最简单最干净的方法就是将 ReturnType 更改为联合。享受。 游乐场链接

type KeyMap<T> = { [key in keyof T]: number };

type DirectSource = {
  value: number;
  otherFieldsAndMethods: any;
};

type IndirectSource<T extends object> = {
  kvMap: KeyMap<T>;
  otherFieldsAndMethods: any;
};

type Source<T extends number | object> = T extends object ? IndirectSource<T> : DirectSource;

// overloads
function sourceAdapter(src: Source<number>): DirectSource;
function sourceAdapter<T extends object>(src: Source<T>, key: keyof T): DirectSource;
function sourceAdapter<T extends object>(src: Source<T>, key: (keyof T)[]): IndirectSource<T>;
function sourceAdapter<T extends object>(
  src: Source<T>,
  key?: keyof T | (keyof T)[]
): IndirectSource<T> | DirectSource {

  if (key) { // According to function overloads Source must be an IndirectSource
    if (key instanceof Array) { // Config and return IndirectSource
      const kvMap = key.reduce((ac, s) => {
        ac[s] = (src as any).kvMap[s];
        return ac;
      }, {} as any);

      // Error here:
      // Type 'T' does not satisfy the constraint 'object'.
      let ret: IndirectSource<T> = {
        kvMap,
        otherFieldsAndMethods: src.otherFieldsAndMethods
      };
      return ret as IndirectSource<T>;
    } else { // Config and return DirectSource
      let directSource = {
        otherFieldsAndMethods: src.otherFieldsAndMethods,
        value: (src as IndirectSource<any>).kvMap[key],
      };
      return directSource; // Error here: assignability
    }
  } else { // Source is a DirectSource, simply return the src.
    return src;
  }
}

推荐阅读