TypeScript编译器无法猜测if语句中的变量类型

yr9zkbsy  于 2023-01-21  发布在  TypeScript
关注(0)|答案(3)|浏览(144)

我目前正在处理leetcode中名为“Merge Two Binary Tree”的问题。
我用TypeScript解决了这个问题,我的解决方案通过了测试,但是编译器显示了这样的错误。

我不明白的是,我清楚地涵盖了每一种情况,TS应该猜测root2在它大喊“root2可能为空”的时候不可能为空。
我是否遗漏了任何情况,或者只是我不理解编译器的行为...?
如果你能给我指明正确的方向,我将不胜感激。
"我的解决方案"

const mergeTrees = (
  root1: TreeNode | null,
  root2: TreeNode | null
): TreeNode | null => {
  if (!root1 && !root2) return null;
  if (root1 && root2)
    return new TreeNode(
      root1.val + root2.val,
      mergeTrees(root1.left, root2.left),
      mergeTrees(root1.right, root2.right)
    );
  if (root1 && !root2)
    return new TreeNode(
      root1.val,
      mergeTrees(root1.left, null),
      mergeTrees(root1.right, null)
    );
  return new TreeNode(
    root2.val,
    mergeTrees(null, root2.left),
    mergeTrees(null, root2.right)
  );
}
gpnt7bae

gpnt7bae1#

TypeScript的编译器所做的流分析是有限制的,虽然 * 你 *(和我们)从代码逻辑中知道root2在最后不可能是null,但TypeScript不知道。
您至少有三个选项:
1.在root2上使用非空类型Assert
1.编写一个显式测试,如果root2null,则该测试将抛出Assert错误,并内联在该函数中
1.使用类型Assert函数

使用非空类型Assert

值表达式末尾的!操作符是一个Assert,表示“我知道这个值永远不会是nullundefined('nullish ')”,所以你可以在所有三个地方使用它:

return new TreeNode(
    root2!.val,
    mergeTrees(null, root2!.left),
    mergeTrees(null, root2!.right)
);

或仅在一个位置中,通过分配给新标识符:

const r = root2!;
return new TreeNode(
    r.val,
    mergeTrees(null, r.left),
    mergeTrees(null, r.right)
);

和所有类型Assert一样,如果你的逻辑出错,TypeScript将无法帮助你,因为它会相信你告诉它的东西。因此,如果逻辑出错,当root2为空时,你将得到一个意外的错误。

root2的显式内联测试

您还可以添加运行时检查并抛出Assert错误:

if (!root2) {
    throw new Error("Assertion failure, 'root2' can't be nullish here");
}
return new TreeNode(
    root2.val,
    mergeTrees(null, root2.left),
    mergeTrees(null, root2.right)
);

这样做的好处是测试了Assert,又提供了一个明确的错误,从而使TypeScript的编译器放心。

类型Assert函数

这和上面的方法差不多,但是是在一个可重用的包中,你可以编写一个可重用的类型Assert函数来测试空值并抛出错误:

// In some utilities module...
function assertIsNotNullish<T>(value: T | null | undefined): asserts value is T {
    if (value === null || value === undefined) {
        throw new Error("Value shouldn't be nullish here");
    }
}

// In your mergeTrees function:
assertIsNotNullish(root2);
return new TreeNode(
    root2.val,
    mergeTrees(null, root2.left),
    mergeTrees(null, root2.right)
);

这有同样的优点,它测试了Assert,也保证了编译器。

s6fujrry

s6fujrry2#

您可以使用nullish coalescing和三元组来缩短代码,这也应该允许编译器解析类型

const mergeTrees = (
  root1: TreeNode | null,
  root2: TreeNode | null
): TreeNode | null => {
  if (!root1 && !root2) return null;
  else {
    return new TreeNode(
      (root1.val ?? 0) + (root2.val ?? 0),
      mergeTrees(root1 ? root1.left : null, root2 ? root2.left : null),
      mergeTrees(root1 ? root1.right : null, root2 ? root2.right : null)
    );
  }
}
pftdvrlh

pftdvrlh3#

我不熟悉 typescript ,但我可以在正确的方向上提供帮助:在C#中有一种情况,你可以有可空变量和不可空变量,通常在你使用这些可空变量之前,你必须让它们变成不可空变量。
如C#中的示例:

DateTime? date = null //nullable variable 
int seconds = 0;

date = DateTime.Now();

if (date != null)
{
    //it's ensured that date is not null, but this still won't compile, 
    //because it's still a nullable variable
    seconds = date.Second;

    //instead, it should be converted to a non-nullable variable. In C#, this goes with '.Value'.
    seconds = date.Value.Second;
}

相关问题