rxjs-hooks
rxjs-hooks copied to clipboard
Question about VoidableEventCallback
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
https://github.com/LeetCode-OpenSource/rxjs-hooks/blob/2b781f12d0c54a079cca9e419153e50e0388fcb0/src/use-event-callback.ts#L6
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?
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)
Same result. It's the VoidableEventCallback giving the wrong types.
What's version of the TypeScript in your project?
I'm using [email protected] and const [callback, flag] = useEventCallback(($events: Observable<React.MouseEvent<HTMLElement> | boolean>) => $events.pipe(mapTo(false)), false) works fine.
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).
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
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.