typescript 如何使数组检查到一个函数而不出错?

alen0pnh  于 2023-06-24  发布在  TypeScript
关注(0)|答案(2)|浏览(123)

我如何将数组检查转换为函数,并且不会在代码中的数组检查位置出现错误?
原来的函数体是“start”,我打算把函数体变成“start1”。然而,当我这样做时,一个错误显示在行中,如下所示。

type JSONRPCParams = object | any[]; // this is in another module where I cannot change the source

async function start(params: JSONRPCParams | undefined) {
    // the intention is to turn the following check into a function
   if (!params || !Array.isArray(params) || params.length === 0) {
       throw new Error('invalid parameter(s) provided');
   }
    
    const assetConfig = params[0]; // this is ok when the check is inline
}

function validateParams(params: JSONRPCParams | undefined) {
   if (!params || !Array.isArray(params) || params.length === 0) {
       throw new Error('invalid parameter(s) provided');
   }
}

async function start1(params: JSONRPCParams | undefined) {
    validateParams(params);    
    const assetConfig = params[0]; // this is not ok, when the check becomes a function
}

错误是:'params'可能是'undefined'。ts(18048)Element隐式具有'any'类型,因为类型'0'的表达式不能用于索引类型'JSONRPCParams'。属性“0”在类型“JSONRPCParams”上不存在。ts(7053)
我也试着写validateParams如下,但它不工作,与上面看到的相同的错误。

if (params && Array.isArray(params) && params.length >= 0) {
    return;
}
throw new Error('Invalid parameter(s) provided');

我如何编写validateParams函数,以便在start1函数中,我不会在注解行上得到错误?
我研究过其他类似的Stackoverflow问题,但它们不能处理这个问题。
您的专业知识和帮助是赞赏。
谢谢你。

6ioyuze2

6ioyuze21#

控制流分析,如真实性检查的结果(如if (params) { })或类型保护函数的结果(如the Array.isArray() method)不会跨函数边界传播。这是为了使编译器易于处理控制流分析而做出的权衡;关于这个一般问题的大量讨论,请参见microsoft/TypeScript#9998。本质上,在函数体内部发生的任何收缩在函数外部都是看不到的,因此在validateParams(xxx)体内检查params参数对函数外部的xxx的表观类型没有影响。
如果您希望这样做,您需要将validateParams注解为Assert函数,并告诉编译器主体如何缩小其参数。例如:

function validateParams(params: JSONRPCParams | undefined): asserts params is any[] {
  if (!params || !Array.isArray(params) || params.length === 0) {
    throw new Error('invalid parameter(s) provided');
  }
}

返回类型是 * Assert predicate * asserts params is any[],这意味着在调用validateParams(xxx)之后,xxx的类型将从可分配给JSONRPCParams | undefined的某个类型缩小到可分配给any[]的某个类型。
请注意,您正在 * 通知 * 编译器该函数如何缩小。编译器不会 * 验证 * 它是否这样做。您可以用任何逻辑替换validateParams()的主体,并且不会出现编译器错误。例如,完全没有逻辑:

function validateParams(params: JSONRPCParams | undefined): asserts params is any[] {
  // eh, it's probably fine
}

所以要小心。
无论如何,现在你的start1()函数将按预期工作:

async function start1(params: JSONRPCParams | undefined) {
  validateParams(params);
  const assetConfig = params[0]; // okay
}

您可以看到validateParams()能够缩小到比any[]更具体的内容,具体取决于输入:

const x = Math.random() < 0.5 ? [1, 2, 3] : undefined;
// const x: number[] | undefined
validateParams(x);
x
// const x: number[]
x.map(x => x.toFixed(1));

这里x已经从number[] | undefined缩小到number[],而不仅仅是any[]
Playground链接到代码

e4yzc0pl

e4yzc0pl2#

我最近遇到了类似的问题,但我想返回一个布尔值。我是这样做的:

export function hasItems<T>(array: Array<T> | undefined): array is Array<T> {
  if (!array || !Array.isArray(array) || array.length === 0) {
    return false;
  }

  return true;
}

我也把这个问题作为参考:What does the is keyword do in typescript?

相关问题