If I declare a type as the following
type UseBoolean = ReturnType<typeof React.useState<boolean>>;
UseBoolean is inferred to be
[boolean | undefined, React.Dispatch<React.SetStateAction<boolean | undefined>>]
But when I look at the source for React.useState,
function React.useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>]
this would make me think UseBoolean should be
[boolean, React.Dispatch<React.SetStateAction<boolean>>]
so why isn't it?
But when I look at the source for
React.useState...
There's a second overload below it, which is being picked up by your type (but we can fix that, keep reading):
function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
It's there to allow for the case where the user doesn't supply an initial value for the state member. Example (playground link):
import React from "react";
// With no initial value:
const ex1 = React.useState<boolean>();
// ^? const ex1: [boolean | undefined, React.Dispatch<SetStateAction<boolean | undefined>>]
// With an initial value:
const ex2 = React.useState<boolean>(true);
// ^? const ex2: [boolean, React.Dispatch<SetStateAction<boolean>>]
I can't think of a way to grab just the first overload, but we can remove the undefined with a mapped type:
type NoUndefinedState<T> =
T extends [infer S | undefined, React.Dispatch<React.SetStateAction<infer S | undefined>>]
? [S, React.Dispatch<React.SetStateAction<S>>]
: never;
Then:
type UseBoolean = NoUndefinedState<ReturnType<typeof React.useState<boolean>>>;
// ^? type UseBoolean = [boolean, React.Dispatch<React.SetStateAction<boolean>>]
type UseString = NoUndefinedState<ReturnType<typeof React.useState<string>>>;
// ^? type UseString = [string, React.Dispatch<React.SetStateAction<string>>]
Playground link
If we want to, we can make it simpler to create these UseXYZ types:
type UseStateTuple<T> = NoUndefinedState<ReturnType<typeof React.useState<T>>>;
Then using it is:
type UseBoolean = UseStateTuple<boolean>;
// ^? type UseBoolean = [boolean, React.Dispatch<React.SetStateAction<boolean>>]
type UseString = UseStateTuple<string>;
// ^? type UseString = [string, React.Dispatch<React.SetStateAction<string>>]
Playground link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With