如何避免在带有Mocha和未定义返回值的TypeScript中使用if / else?

时间:2019-07-16 23:08:05

标签: typescript mocha chai

一个可以网状显示并返回另一个样条曲线的样条曲线。至少在大多数时候。

export default class Spline {
  public reticulatedCount: number;

  constructor(parent?: Spline) {
    this.reticulatedCount = parent && parent.reticulatedCount + 1 || 0;
  }

  public reticulate(): Spline | undefined {
    return new Spline(this);
  }
}
import { assert, expect } from 'chai';
import Spline from '../src/spline';

describe("Spline", () => {
  const spline = new Spline();

  it("returns a new spline", () => {
    const reticulatedSpline = spline.reticulate();
    expect(reticulatedSpline).to.not.be.null;
    expect(reticulatedSpline.reticulatedCount).to.eq(1);
  });
});

error TS2532: Object is possibly 'undefined'.失败

/Users/dblock/source/ts/typescript-mocha/node_modules/ts-node/src/index.ts:245
    return new TSError(diagnosticText, diagnosticCodes)
           ^
TSError: ⨯ Unable to compile TypeScript:
test/spline.spec.ts:18:12 - error TS2532: Object is possibly 'undefined'.

18     expect(reticulatedSpline.reticulatedCount).to.eq(1);

解决方法是测试中的反模式,if

  it("returns a new spline", () => {
    const reticulatedSpline = spline.reticulate();
    if (reticulatedSpline) {
      expect(reticulatedSpline.reticulatedCount).to.eq(1);
    } else {
      expect(reticulatedSpline).to.not.be.null;
    }
  });

如何在不禁用strictNullChecks的情况下解决此问题?

https://github.com/dblock/typescript-mocha-strict-null-checks中的代码。

3 个答案:

答案 0 :(得分:2)

您可以使用non-null!)运算符。

it("always can be reticulated again", () => {
  const reticulatedSpline = spline.reticulate();
  expect(reticulatedSpline).to.not.be.null;
  expect(reticulatedSpline!.reticulatedCount).to.eq(1);
});

如文档所述:

  

[You]可能会在类型检查器无法得出结论的情况下断言其操作数是非null且未定义的

Source

答案 1 :(得分:1)

TypeScript 3.7的更新示例,其中引入了“断言签名”:

/**
 * Use in place of `expect(value).to.exist`
 *
 * Work-around for Chai assertions not being recognized by TypeScript's control flow analysis.
 * @param {any} value
 */
export function expectToExist<T>(value: T): asserts value is NonNullable<T> {
  expect(value).to.exist;
  if (value === null || value === undefined) {
    throw new Error('Expected value to exist');
  }
}

参考:

答案 2 :(得分:0)

由于在这些示例中.to.not.be.null不会影响代码流,因此TS当前无法推断它对传递给它的参数进行了更改。使用user-defined type guards有一种依赖于代码流的方式。

function assertNotNull<T>(v: T | null): v is NonNullable<T> {
    if (!v) throw new Error();
    return true
}

declare const maybeAString: string | undefined

function ex() {
    // Doesn't work because TS has no way to know that this will throw
    assertNotNull(maybeAString)
    maybeAString

    // Control flow analysis knows that this function will validate that maybeAString is definitely not null
    if(assertNotNull(maybeAString)) {
        maybeAString // now definitely a string
    }

    // control flow analysis knows that the branch where maybeAString isn't not null (aka is null) returns, so the main path must be non-null
    if(!assertNotNull(maybeAString)) return

    maybeAString // now definitely a string
}

playground