参数'a'和'left'的类型不兼容

时间:2019-04-04 17:47:35

标签: typescript generics

我是TypeScript的新用户,请耐心

⚽目标

我正在尝试创建一种方法,该方法通过接受number | string | object数组和可选的比较函数(类似于Array.sort的比较函数)来返回MinHeap(优先级队列)。

⩴⩴⩴⩴⩴⩴⩴⩴⩴⩴

有问题的代码

Repl.it上的演示。

下面的

useHeap接受通用数组类型,可选的comp比较器也接受type参数。

function isNumberArray(o: any[]): o is number[] {
  return o.every(n => typeof n === "number");
}
function isStringArray(o: any[]): o is string[] {
  return o.every(n => typeof n === "string");
}

// type Unpack<T> = T extends (infer R)[] ? R : T;
interface Comparor<T> {
  (left: T, right: T): number;
}

function useHeap<T>(args: T[], comp?: Comparor<T>): T[] {
  if (!comp) {
    if (isStringArray(args)) {
      comp = (a, b) => a < b ? -1 : a > b ? 1 : 0;
      console.log(`args is an STRING array!`, ...args, comp);
    } else if (isNumberArray(args)) {
      console.log(`args is a NUMBER array!`, ...args, comp);
    } else {
      // throw new Error("You need to pass a comparor for an object array");
      console.log(`args is an OBJECT array!`, ...args, comp);
    }
  } else {
    console.log(` comp available!`, ...args, comp);
  }

  // turn the T[] into a heap using the Comparor

  return [] as T[];
}

⩴⩴⩴⩴⩴⩴⩴⩴⩴⩴

‍♂️问题

当我像下面这样呼叫useHeap时,

useHeap([1, 2, 3]);
useHeap([1, 2, 3], (a, b) => a * b);
useHeap(["c1", "a1", "b1"]);
useHeap(["c", "a", "b"], (a, b) => a < b ? -1 : a > b ? 1 : 0);
useHeap([{ id: 1, weight: 10 }, { id: 2, weight: 20 }]);
useHeap([{ id: 1, weight: 10 }, { id: 2, weight: 20 }], (a, b) => a.weight - b.weight);

以下内容打印在控制台中。

TypeScript v3.3.3 linux/amd64
args is a NUMBER array! 1 2 3 undefined
 comp available! 1 2 3 (a, b) => a * b
args is an STRING array! c1 a1 b1 (a, b) => a < b ? -1 : a > b ? 1 : 0
 comp available! c a b (a, b) => a < b ? -1 : a > b ? 1 : 0
args is an OBJECT array! { id: 1, weight: 10 } { id: 2, weight: 20 } undefined
 comp available! { id: 1, weight: 10 } { id: 2, weight: 20 } (a, b) => a.weight - b.weight

问题是当我尝试为数字类型的数组分配默认比较器时。

function useHeap<T>(args: T[], comp?: Comparor<T>): T[] {
  if (!comp) {
    if (isStringArray(args)) {
      comp = (a, b) => a < b ? -1 : a > b ? 1 : 0;
      console.log(`args is an STRING array!`, ...args, comp);
    } else if (isNumberArray(args)) {
      //  These throw an error
+      comp = (a, b) => a - b;
+      comp = (a: number, b: number) => a - b;
      console.log(`args is a NUMBER array!`, ...args, comp);
    } else {
    }
  } else {
    console.log(` comp available!`, ...args, comp);
  }

  // turn the T[] into a heap using the Comparor

  return [] as T[];
}

第一次尝试

将鼠标悬停在comp = (a, b) => a - b上,

  

[打字稿]算术运算的左侧必须为'any','number','bigint'或枚举类型。

     

(参数)a:T

first error

第二次尝试

comp = (a: number, b: number) => a - b;显示时

  

[打字稿]   类型“(a:数字,b:数字)=>数字”不可分配给“比较器”类型。     参数“ a”和“ left”的类型不兼容。       类型“ T”不可分配给类型“数字”。   (参数)comp:比较器|未定义

second error

⩴⩴⩴⩴⩴⩴⩴⩴⩴⩴

主要问题

  1. 即使if (isNumberArray(args))通过,为什么仍能识别数字?
  2. 如何使comp的类型正确识别?

其他上下文

请不要犹豫,让我知道如何用更像TypeScript的方式编写它

1 个答案:

答案 0 :(得分:1)

我会说这不是一个很好的解决方案。在泛型方法中使用运行时类型检查通常是不好的做法(“泛型”方法的要点是,逻辑应适用于所提供的任何类型)。但是,这是一种更友好的类型的解决方案,可帮助您解决当前的问题:

const stringComparer = <T extends string>(a: T, b: T) => a < b ? -1 : a > b ? 1 : 0;
const numberComparer = <T extends number>(a: T, b: T) => a - b;
function getDefaultComparitor<T>(args: T[]): Comparor<T> | undefined {
    if (isStringArray(args)) {
      return stringComparer as Comparor<T>;
    } else if (isNumberArray(args)) {
      return numberComparer as Comparor<T>;
    }

    return undefined;
}

function useHeap<T>(args: T[], comp?: Comparor<T>): T[] {
  if (!comp) {
    comp = getDefaultComparitor(args);
    if (!comp) {
      console.log(` unable to determine default comparitor!`, ...args);
    }
  } else {
    console.log(` comp available!`, ...args, comp);
  }

  // turn the T[] into a heap using the Comparor
  return [] as T[];
}

注意:as Comparer<T>是代码气味,应提醒您这里有些古怪。

一个更好的解决方案是使用重载在编译时提供错误:

function useHeap(args: string[]);
function useHeap(args: number[]);
function useHeap<T>(args: T[], comp: Comparor<T>);
function useHeap<T>(args: T[], comp?: Comparor<T>) {
  // same as above
}

现在,此测试用例将生成错误:

useHeap([{ id: 1, weight: 10 }, { id: 2, weight: 20 }]);
  

类型'{ id: number; weight: number; }'不能分配给类型'number'。

这不是一个非常有用的错误消息,但是至少可以更早地发现它。您可以通过以下一些更细微的重载来改善错误消息:

type ComparitorParameter<T> = T extends string|number ? []|[Comparor<T>] : [Comparor<T>];

function useHeap<T>(args: T[], ...comp:ComparitorParameter<T>);
function useHeap<T>(args: T[], comp?: Comparor<T>): T[] {
  // same as above
}

无效的测试用例现在将产生一个更加直观的错误:

  

预期2个参数,但得到1个。