'Typescript: How to have a conditional return type and have values types vary together
I have a useSelectedToggle hook which allows coupling of the UI state to the open/closed state of the dialog it will appear in. If the toggle is open then the value is defined (T), if the toggle is closed then the value is null. How would I type this more strictly so that if isOpen is true then selectedValue is T and vice versa? Likewise, if isOpen is false then selectedValue has to be null. Thanks!
import React from 'react';
import { useToggle } from '../useToggle/useToggle';
export interface UseSelectedToggleType<T> {
isOpen: boolean;
openWith: (val: T) => void;
close: () => void;
selectedValue: T | null;
}
// Couple state used in a dialog to its open/closed state
export function useSelectedToggle<T>(initialValue?: T): UseSelectedToggleType<T> {
const [selectedValue, setSelectedValue] = React.useState<T | null>(initialValue ?? null);
const { open: toggleOpen, isOpen, close } = useToggle();
const openWith = (val: T) => {
setSelectedValue(val);
};
const closeWith = () => {
close();
setSelectedValue(null);
};
React.useEffect(() => {
if (selectedValue !== null) {
toggleOpen();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedValue]);
return {
isOpen,
openWith,
close: closeWith,
selectedValue,
};
}
Solution 1:[1]
Typescript has conditional types, you can use this if you want to provide some simple logic in your typescript type. Here doc you can read
This is the simple example they are using:
type Cat = { meows: true };
type Dog = { barks: true };
type Cheetah = { meows: true; fast: true };
type Wolf = { barks: true; howls: true };
// We can create a conditional type which lets extract
// types which only conform to something which barks.
type ExtractDogish<A> = A extends { barks: true } ? A : never;
// Then we can create types which ExtractDogish wraps:
// A cat doesn't bark, so it will return never
type NeverCat = ExtractDogish<Cat>;
// A wolf will bark, so it returns the wolf shape
type Wolfish = ExtractDogish<Wolf>;
So in your use case, I think you can do this
type isOpenSelected<A> = A extends {isOpen:true} ? A : never
export interface UseSelectedToggleType<T> {
isOpen: boolean;
openWith: (val: T) => void;
close: () => void;
selectedValue: isOpenSelected<T>;
}
I wrote the code in a sluggish manner, but I hope you can fix your issue with this information. I hope I was helpful
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Dakshesh Jain |
