Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I avoid an if/else in TypeScript with mocha and undefined returns?

A spline that can be reticulated and return another spline. Most of the time at least.

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);
  });
});

Fails with 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);

The workaround is an anti-pattern in tests, an 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;
    }
  });

How can this be solved without disabling strictNullChecks?

Code in https://github.com/dblock/typescript-mocha-strict-null-checks.

like image 650
dB. Avatar asked Nov 19 '25 14:11

dB.


2 Answers

You can use the non-null (!) operator.

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

As the documentation says:

[You] may be used to assert that its operand is non-null and non-undefined in contexts where the type checker is unable to conclude that fact

Source

like image 178
guzmonne Avatar answered Nov 21 '25 09:11

guzmonne


Updated example for Typescript 3.7 which introduced "assertion signatures":

/**
 * 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');
  }
}

References:

  • https://github.com/microsoft/TypeScript/pull/32695
  • https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html
like image 21
Christopher Bonhage Avatar answered Nov 21 '25 10:11

Christopher Bonhage