Issue
The props' type is correctly inferred as void
, but it only works if I pass undefined
as an argument.
Is it possible not to pass anything as an argument??
const openModal = <T>(
modal: (props:T) => void,
props: {} extends Omit<T, "onClose"> ? void : Omit<T, "onClose">
) => {
return;
};
const ModalOne = (props: {onClose: () => void}) => {
return;
}
const ModalTwo = (props: {text: string, onClose: () => void}) => {
return;
}
openModal(ModalOne) // not working (Expected 2 arguments, but got 1. An argument for 'props' was not provided.)
openModal(ModalOne, {}) // not working (Argument of type '{}' is not assignable to parameter of type 'void'.)
openModal(ModalOne, undefined) // working
openModal(ModalTwo, {text: 'test'}) // working
Solution
The use of the void
type to make a function parameter optional (as implemented in microsoft/TypeScript#27522) is unfortunately not fully supported. It doesn't tend to work with generics, as described in microsoft/TypeScript#29131. This is considered a bug in TypeScript, but it's been around for a long time with no sign that it will be fixed anytime soon or ever. I'd recommend that you avoid using void
for this reason (indeed void
behaves weirdly enough in so many circumstances that you should probably always pause a little to be sure it's really what you want to use).
Instead, you can implement "possibly optional" function parameters by using a rest parameter of a tuple type, where the tuple type depends on whatever condition you want to check. For example:
const openModal = <T,>(
modal: (props: T) => void,
...[props]: {} extends Omit<T, "onClose"> ?
[props?: Omit<T, "onClose">] :
[props: Omit<T, "onClose">]
) => {
return;
};
So technically openModal
is a variadic function whose rest parameter ...[props]
is either of type [props?: Omit<T, "onClose">]
(a one-element tuple whose element is optional) or of type [props: Omit<T, "onClose">]
(a one-element tuple whose element is required). Since ...[props]
uses destructuring assignment, then this ends up giving you a props
parameter inside the function whose type is either Omit<T, "onClose">
or Omit<T, "onClose"> | undefined
depending on the type T
.
When you call it, if the empty object type is assignable to T
, then you can call openModal()
with one argument. Otherwise two arguments are required:
const ModalOne = (props: { onClose: () => void }) => { };
openModal(ModalOne); // okay
openModal(ModalOne, {});// okay
openModal(ModalOne, undefined); // okay
const ModalTwo = (props: { text: string, onClose: () => void }) => { };
openModal(ModalTwo); // error
openModal(ModalTwo, {}); // error
openModal(ModalTwo, undefined); // error
openModal(ModalTwo, { text: 'test' }); // okay
const ModalThree = (props: { text?: string, onClose: () => void }) => { };
openModal(ModalThree); // okay
openModal(ModalThree, {}); // okay
openModal(ModalThree, undefined); // okay
openModal(ModalThree, { text: 'test' }); // okay
Looks like the behavior you wanted.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.