noseratio
noseratio

Reputation: 61716

Typing a nullable callback variable in TypeScript

Is it possible to simplify the declaration of resolve variable in the TypeScript code below, without sacrificing type safety? I'm new to TypeScript, please bear with me.

The goal is to save the promise resolver callback, passed to to the executor function, so it can be invoked later outside it. What I've come up with is let resolve: ((v: number) => void) | null = null. Try it in the TS Playground:

function timeout(ms: number): Promise<number> {
  // save the promise's resolver to be invoked later
  let resolve: ((v: number) => void) | null = null; // <===
  const p = new Promise<number>(r => resolve = r);

  const start = performance.now();
  setTimeout(() => resolve!(performance.now() - start), ms);
  return p;
}

If I don't make it a union type with null, TypeScript will legitly complain that resolve might be uninitialized when I invoke it later: setTimeout(() => resolve(...), ms). Is there a more concise way to do this, without introducing any or TypeScript errors?

To clarify, the above example is a bit contrived, it could be easily simplified like this:

function timeout(ms: number): Promise<number> {
    const start = performance.now();
    return new Promise<number>(r => 
        setTimeout(() => r(performance.now() - start)));
}

The question is however about TypeScript syntax. The actual code does need to save the promise's resolve and reject callbacks to implement the Deferred pattern.

Upvotes: 1

Views: 212

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370989

One way to make it more concise is to remove the nulls and not assign anything to resolve at first:

function timeout(ms: number): Promise<number> {
    let resolve: (v: number) => void;
    const p = new Promise<number>(r => resolve = r);

    const start = performance.now();
    setTimeout(() => resolve(performance.now() - start), ms);
    return p;
}

It may look a bit odd, but this does happen to work with any ! assertion.

If you happen to be able to return the Promise immediately, you could slim it down to

function timeout(ms: number) {
    let resolve: (v: number) => void;
    return new Promise<number>(r => {
        resolve = r;
        const start = performance.now();
        setTimeout(() => resolve(performance.now() - start), ms);
    });
}

Upvotes: 1

Related Questions