首页 > 解决方案 > 参数“a”和“left”的类型不兼容

问题描述

我是一个新的 TypeScript 用户,所以请多多包涵

⚽ 目标

我正在尝试通过接受一个数组number | string | object和一个可选的比较函数(有点像Array.sort的函数)来创建一个返回 MinHeap (优先队列)的方法。

⩴⩴⩴⩴⩴⩴⩴⩴⩴⩴

有问题的代码

Repl.it上提供了一个演示。

useHeap下面接受泛型数组类型,可选的comp比较器也接受类型参数。

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节目上,

[typescript] 算术运算的左侧必须是“any”、“number”、“bigint”类型或枚举类型。

(参数)a:T

第一个错误

第二次尝试

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

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

第二个错误

⩴⩴⩴⩴⩴⩴⩴⩴⩴⩴

主要问题

  1. 为什么即使if (isNumberArray(args))通过了号码也会被识别?
  2. 如何comp正确识别 's 类型?

附加上下文

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

标签: typescriptgenerics

解决方案


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

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 个。


推荐阅读