Joe
Joe

Reputation: 3620

Typescript typing for function callback usable with a function declaration

Is it possible to type a callback function in Typescript that encodes the following 2 invariants in the type system:

  1. if err == null then x is of type T
  2. if err != null then x must not be referenced (never?)
type Callback<T> = {
  (error: null, value: T): void;
  (error: Error): void;
};

// The function that takes the typed callback.
function incrementAsync(x: number, cb: Callback<number>): void {
  if (x === 0) {
    return cb(new Error());
  }

  cb(null, x + 1); // should type-check
  cb(new Error()); // should type-check

  cb(new Error(), 1) // should fail type-check
  cb(null);          // should fail type-check
}

Here's the function signature that I really want:

// Error: This overload signature is not compatible 
//        with its implementation signature.
function definedCb1(e: Error);
function definedCb1(e: null, x: number): void {}
incrementAsync(10, definedCb1); // error with strictNullChecks

Here's a looser function signature that type checks

// BAD because types are too loose but this passes type-check.
function definedCb2(e: Error|null, x?: number): void {
  if (e === null) {
    // Is there a way to type this function, so the ! is not necessary?
    console.log(x! + 1);
    return;
  }
  // error because x might be undefined.
  console.log(x + 2);
}
incrementAsync(20, definedCb2) // Works

Typescript Playground Link enable strictNullChecks and noImplicitAny.

Upvotes: 3

Views: 869

Answers (1)

Shaun Luttin
Shaun Luttin

Reputation: 141622

If you turn on strictNullChecks, then this meets your requirements.

interface Callback<T> {
  (error: Error): void;
  (error: null, value: T): void;
}

function incrementAsync(x: number, cb: Callback<number>): void {
  cb(new Error());          // works
  cb(null, x + 1);          // works

  cb(null);                 // error
  cb(new Error(), 'foo');   // error
  cb(null, 'foo');          // error
  cb(new Error(), 10);      // error    
}

Here it is in the playground.

The strictNullChecks flag prevents all types from being nullable types.

Upvotes: 1

Related Questions