网站搜索

javascript - 为什么在这种特定情况下,类型断言不能消除“元素隐式具有 'any' 类型”?


我有这段代码(有点复杂,但我需要它保持这种方式和这种类型结构)。基本上,我想让一个数组按这种方式排序:

  • 首先按物品类型
  • 然后由所有者
  • 然后按数值

即,如果有两个具有相同 itemType 的元素,则会考虑 owner,依此类推。这只是以防万一,不应与所讨论的 tsc 错误有任何直接关系。

'use strict';

type Item = {
  itemType: 'itemTypeA' | 'itemTypeB';
  belongsTo: 'ownerA' | 'ownerB';
  value: number;
};

type Argument = {
  items: Item[];
};

const a: Argument = {
  items: [
    {
      itemType: 'itemTypeB',
      belongsTo: 'ownerA',
      value: 0,
    },
    {
      itemType: 'itemTypeA',
      belongsTo: 'ownerB',
      value: 1,
    },
    {
      itemType: 'itemTypeB',
      belongsTo: 'ownerA',
      value: 10,
    },
    {
      itemType: 'itemTypeB',
      belongsTo: 'ownerA',
      value: 0,
    },
  ],
};
console.log(testFunc(a));

function testFunc(arg: Argument): string {
  const sortByData = new Map([
    ['itemType', { itemTypeA: 0, itemTypeB: 1, },],
    ['belongsTo', { ownerA: 0, ownerB: 1, },],
    ['value', null,],
  ]);
  arg.items.sort((a, b) => {
    for (const [propName, sortValues] of sortByData) {
      //// @ts-ignore: failed to make it work with type checks
      const aValue = sortValues ? sortValues[a[propName]] as any as number : a[propName];
      ////// @ts-ignore: failed to make it work with type checks
      //const bValue = sortValues ? sortValues[b[propName]] : b[propName];
      //if (aValue > bValue) {
      //  return 1;
      //} else if (aValue < bValue) {
      //  return -1;
      //}
    }
    return 0;
  });
  console.log('AAA', arg.items);
  return '123';
}

问题是这行代码:

const aValue = sortValues ? sortValues[a[propName]] as any as number : a[propName];
                            ~~~~~~~~~~~~~~~~~~~~~~~

导致此错误:

由于类型为 'any' 的表达式不能用来索引类型 '{ itemTypeA: number; itemTypeB: number; ownerA?: never; ownerB?: never; } | { ownerA: number; ownerB: number; itemTypeA?: never; itemTypeB?: …',元素隐式具有 'any' 类型

我知道这个(~~下划线的)表达式是一个数字。仅仅断言它是一个数字对我来说就足够了,但即使这样也行不通。

那么有人能解释一下,为什么在这种情况下类型断言不起作用,或者更一般地,解决这个错误的最佳方法是什么?(我唯一找到的方法就是使用 // @ts-ignore: failed to make it work with type checks 禁用类型检查)

答案#1

你在不同位置有多个类型错误,所以你需要使用多个类型断言来解决它们。在类似 sortValues[a[propName]] 的情况中,你依赖于 propName 被视为 a 的有效键,并且 a[propName] 被视为 sortValues 的有效键。对于你写的代码,这两个都不成立。关于为什么会这样在这里不在讨论范围之内,但如果你想通过类型断言来解决,你需要对其中每一个进行断言:

const aValue = sortValues ?
    sortValues[a[propName as keyof Item] as keyof typeof sortValues] as number :
    a[propName as keyof Item];

如果你只是尝试通过关闭类型检查来解决错误,你可以通过使用 any 类型来减少断言的数量:

arg.items.sort((a: any, b) => {
    for (const [propName, sortValues] of sortByData as Map<string, any>) {
        const aValue = sortValues ? sortValues[a[propName]] : a[propName];
    }
    return 0;
});

你可以使用 //@ts-ignore 注释指令来抑制错误,但除非你已经用尽所有其他选项,否则我绝不会推荐这样做。这样做并不会关闭类型检查;它只是禁止 显示 错误。虽然错误可能会显示在代码的某一行,但潜在问题可能并不局限于该行……所以你可能会在代码的其他地方看到奇怪的问题。换句话说,编译器错误通常表明存在某些实际问题,而如果你对其使用 //@ts-ignore,这个实际问题仍然存在。对于像类型断言这样微不足道的事情,可能不会有这种非局部的影响,但对于微不足道的类型断言,你应该直接使用类型断言。正如文档中所说,"我们建议你 非常谨慎 地使用这些注释。"

代码的 Playground 链接