如何将空检查拉入打字稿中的功能

How to pull null check into function in typescript

我有一些代码,其中有几个函数需要对对象进行空检查,然后对其进行处理,或者如果它为空则抛出错误,如下所示:

interface SomeObject {
    doThingOne: () => string;
    doThingTwo: () => string;
}

let object: SomeObject | null; 

function doThingOne() {
  if(!object) {
    throw new Error('Object is null!');
  }
  // Typescript knows that object is not null, so it's fine with accessing its properties/methods
  return object.doThingOne();
}

function doThingTwo() {
  if(!object) {
    throw new Error('Object is null!');
  }
  return object.doThingTwo();
}

我想将空检查提取到函数中以减少重复代码,如下所示:

interface SomeObject {
    doThingOne: () => string;
    doThingTwo: () => string;
}

let object: SomeObject | null; 

function requireObject() {
  if(!object) {
    throw new Error('Object is null!');
  }
}

function doThingOne() {
  requireObject();
  // Typescript no longer knows that object is defined, so it gets mad at the next line
  return object.doThingOne();
}

function doThingTwo() {
  requireObject();
  return object.doThingTwo();
}

然而,当我这样做时,typescript 不再知道在检查之后,object 确实存在。有没有一种干净的方法可以做到这一点?

看起来很相似,但实际上不会让我节省很多代码重复,因为我仍然需要设置一个 if

您可以使用 ! 运算符告诉编译器 object 肯定会包含一个值。

return object!.doThingOne();

我尽量避免这种事情。以后很容易惹上麻烦。相反,我会将 requireObject 函数定义为始终 return 一个对象。

function requireObject(): object {
    if (!object) {
        throw new Error('Object is null!');
    }
    return object;
}

然后你可以这样称呼它:

object = requireObject();

因为 requireObject 总是 return 是一个 object 类型,编译器会知道此时 object 不是 null.

现在唯一的问题是,因为object实际上是一个TypeScript类型,所以代码有点混乱。它应该可以工作(我还没有实际尝试过),但您可能需要考虑将变量命名为其他名称。

无法编写 requireObject() 并影响 object 的类型,因为编译器不会以任何方式将 requireObject() 视为与 object 相关. (如果编译器能够管理由于函数内部封闭变量的突变而导致的所有可能的状态变化,那就太好了,但这在计算上是不可行的。请参阅 microsoft/TypeScript#9998 了解一般问题。)

你可以做的是将 object 传递给 requireObject() 并注释 requireObject() 是一个 assertion function. Assertion functions must be implemented as void-returning (so they don't return anything), and the annotated return type is an "assertion predicate" of the form asserts x is Y (where x is the name of one of the function parameters) or just asserts x if you want to say that x is truthy.

所以这给了我们:

function requireObject(x: any): asserts x {
  if (!x) {
    throw new Error('something is falsy here');
  }
}

现在我们可以毫无错误地做到这一点:

let object: SomeObject | null;

function doThingOne() {
  requireObject(object);
  return object.doThingOne(); // okay
}

function doThingTwo() {
  requireObject(object);
  return object.doThingTwo(); // okay
}

Playground link to code