简体   繁体   中英

Typescript: How to define a type for a callback that shouldn't return anything?

I mean it should return undefined or no return at all. Typical for safe callbacks.

Tryout 1:

declare const fn1: (cb: () => void) => void;
fn1(() => '123'); // no error

Oops. It didn't work out. string is applicable to void . Ok...

Tryout 2:

declare const fn2: (cb: () => unknown) => void;
fn2(() => '123'); // no error

Oops. The same. Ooookay.

Tryout 3:

declare const fn3: (cb: () => undefined) => void;
fn3(() => '123'); // error: Type 'string' is not assignable to type 'undefined'

Nice, Ok: go with it:

fn3(() => undefined); // okay
fn3(() => {}); // error: Type 'void' is not assignable to type 'undefined'

Hm. It's not what I'm looking for.

Okay, what's about this crazy idea? Tryout 4:

declare const fn4: (cb: () => void | undefined) => void;
fn4(() => undefined); // okay
fn4(() => { }); // okay
fn4(() => '123'); // error: Type 'string' is not assignable to type 'undefined'

Wow. It's what I wanted.

But it looks like a dirty hack. Why on the Earth void doesn't complain about string , but void | undefined void | undefined does it? Is it a bug?

Can I rely on this behavior? Wouldn't it be fixed in some future versions of TS? Is there any better way to accomplish the same?

UPDATED 2

a callback that shouldn't have any args even optional and shouldn't return anything should be defined like that:

type callback = (...a: void[]) => void | undefined;

const func = (a: callback) => { 
    a();
};

const test1 = func(() => { }); // works
const test2 = func((a?: string) => { }); // fails
const test2 = func(() => '5'); // fails

UPDATED

after discussion in comments was found out that the goal is to get a callback not with ignored return, but with guarantee that it won't return anything else and it can be passed to other functions that change behavior based on returned value.

In this case I would suggest to add a small helper function that really ignores any returned value.

const shutUp = <T extends (...args: any) => void>(func: T): ((...args: Parameters<T>) => void) => (...args: any[]) => {
    func(...args);
};

const shutUpNoArgs = <T extends () => void>(func: T): ((...args: never) => void) => () => {
    func();
};

const test = shutUp((a: string) => 5);

const r1 = test('5'); // works, r1 is void.
const r2 = test(5); // doesn't work.

_.forEach(data, shutUp(callback));

ORIGINAL

You shouldn't be so strict and to worry too much about that.

void means we don't care about return, should we if we don't rely on it?

It's the same as to say that we don't accept a function because it doesn't require our a argument despite it can handle the case.

declare const fn1: (cb: (a: string) => void) => void;

fn1(() => {
}); // no error even we don't have `a` in our callback.

Docs: https://www.typescriptlang.org/docs/handbook/basic-types.html#void

void is a little like the opposite of any : the absence of having any type at all.

Therefore, its behavior won't be changed you can use it in a union with undefined .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM