Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about VoidableEventCallback #60

Open
crimx opened this issue Jul 16, 2019 · 6 comments

Comments

@crimx
Copy link

@crimx crimx commented Jul 16, 2019

I was trying write something like

const [callback, flag] = useEventCallback<
  React.MouseEvent<HTMLElement> | boolean,
  boolean
>($events => $events.pipe(mapTo(false)), false)

callback(true)

And there was type error.

Argument of type 'true' is not assignable to parameter of type 'false & true & MouseEvent<HTMLElement, MouseEvent>'.
  Type 'true' is not assignable to type 'false'.ts(2345)

Checking the source

export type VoidableEventCallback<EventValue> = EventValue extends void ? () => void : (e: EventValue) => void

It seems like for example

type Callback = VoidableEventCallback<string | boolean>

will be resolved as

type Callback = ((e: string) => void) | ((e: false) => void) | ((e: true) => void)

which means e has to be string & boolean. Is this a bug or am I missing something here?

@Brooooooklyn

This comment has been minimized.

Copy link
Member

@Brooooooklyn Brooooooklyn commented Jul 16, 2019

It's seem like a TypeScript behavior.
Maybe you should let TypeScript resolve the generic type params itself.

const [callback, flag] = useEventCallback(($events: Observable<React.MouseEvent<HTMLElement> | boolean>) => $events.pipe(mapTo(false)), false)

callback(true)
@crimx

This comment has been minimized.

Copy link
Author

@crimx crimx commented Jul 16, 2019

Same result. It's the VoidableEventCallback giving the wrong types.

@Brooooooklyn

This comment has been minimized.

Copy link
Member

@Brooooooklyn Brooooooklyn commented Jul 16, 2019

What's version of the TypeScript in your project?
I'm using TypeScript@3.5.2 and const [callback, flag] = useEventCallback(($events: Observable<React.MouseEvent<HTMLElement> | boolean>) => $events.pipe(mapTo(false)), false) works fine.

@crimx

This comment has been minimized.

Copy link
Author

@crimx crimx commented Jul 16, 2019

I'm using 3.5.2 too.

const [callback, flag] = useEventCallback(($events: Observable<React.MouseEvent<HTMLElement> | boolean>) => $events.pipe(mapTo(false)), false) is fine. It's callback(true) got the error.

Found this on the doc, might be the reason?

Conditional types in which the checked type is a naked type parameter are called distributive conditional types. Distributive conditional types are automatically distributed over union types during instantiation. For example, an instantiation of T extends U ? X : Y with the type argument A | B | C for T is resolved as (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y).

@Austaras

This comment has been minimized.

Copy link

@Austaras Austaras commented Sep 25, 2019

It's a known trick of Typescript, that is when a union is put in a contravariant place(like function parameter) in conditional type it will be turned into a intersection type. So boolean = true | false is turned into true & false aka never

There is one easy and tricky solution

type VoidableEventCallback<EventValue> = {
  0: () => void
  1: (e: EventValue) => void
}[EventValue extends void ? 0 : 1]

But @Brooooooklyn what is the purpose of this type in the first place? In Typescript a function with a void parameter is quite identical to a function with no parameter

declare function foo(a: void): void
foo()
foo(1) // error
@crimx

This comment has been minimized.

Copy link
Author

@crimx crimx commented Sep 25, 2019

Very insightful! Didn't know a trick like this. Wish you'd comment earlier so that I don't have to make my own wheels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.