Reputation: 3620
Is it possible to type a callback function in Typescript that encodes the following 2 invariants in the type system:
T
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
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