I’m having trouble with typescript not throwing type errors in my code. I have a function registerIpcChannel
that registers an IPC channel with a specific handler. The handler should match the types defined in the IpcChannel
interface. This works for the most part, however, when I intentionally return undefined
or null
from the handler, typescript does not throw a type error as expected. Here’s a simplified version of my code:
/**
* Interface that describes an IPC channel
* @template P - Type of the parameters for the handler
* @template R - Type of the return value for the handler
*/
interface IpcChannel<P extends any[], R> {
name: string;
}
/**
* Function that takes in a IpcChannel interface, handler function, registers it
*/
const registerIpcChannel = <P extends any[], R>(
ipcChannel: IpcChannel<P, R>,
handler: (...params: P) => R
): void => {
// Do things...
};
/**
* Create definition of an IPC channel
* Accepts a single parameter that is a string, returns a string
*/
const testIpcChannel: IpcChannel<[string], string> = {
name: '/foo/bar',
};
//
// Testing the register function
//
/**
* Register the IPC channel
* Sanity check passes, no type error is thrown.
*/
registerIpcChannel(testIpcChannel, (arg) => {
return 'This returns a string: ' + arg;
});
/**
* Test with returning the wrong type
* Sanity check passes, a type error is thrown because a number is returned.
*/
registerIpcChannel(testIpcChannel, (arg) => {
return 1;
});
/**
* Test with returning undefined
* Sanity check FAILS, this should throw a type error
*/
registerIpcChannel(testIpcChannel, (arg) => {
return undefined;
});
The last registerIpcChannel
call that returns undefined
should be throwing a type error, however it is not.
I found that if I constrain the return type of handler
to NonNullable
, that will make a type error be thrown. However that then breaks situations where the handler function returns void
.
const registerIpcChannel = <P extends any[], R>(
ipcChannel: IpcChannel<P, R>,
handler: (...params: P) => NonNullable<R> // + Added `NonNullable`
): void => {
// Do things...
};
/**
* Void test
* Fails, this now throws an error after adding `NonNullable`
*/
const voidIpcChannel: IpcChannel<[string], void> = {
name: '/foo/bar',
};
registerIpcChannel(voidIpcChannel, (arg) => {
return;
});
Interestingly, it seems that if I refactor the way things are structured by moving the handler definition to the IpcChannel
interface then things behave how I would expect.
//
// Handler on the `IpcChannel` interface
//
interface IpcChannel2<P extends any[], R> {
name: string;
handler: (...args: P) => R;
}
/**
* Sanity check passes, a type error is thrown.
*/
const test: IpcChannel2<[string], string> = {
name: '/foo/bar',
handler: (arg) => {
return undefined;
},
};
However, do to limitations I cannot do this. I need to have the handler function defined separately from the IpcChannel
interface.
Here’s a stackblitz to test things out. Thanks.
upset-toaster is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.