type TabWithSetIndex = (props: TabContentProps) => React.ReactElement;
type TabWithoutSetIndex = () => React.ReactElement;
interface ProjectCardProps {
tabs: Array<{
tabContent: TabWithSetIndex | TabWithoutSetIndex;
}>;
}
How to validate whether tabContent
is of type TabWithSetIndex
or TabWithoutSetIndex
?
CodePudding user response:
A couple of options for you:
- Function
length
with a type predicate - A discriminated union (with or without a type predicate)
Function length
with a type predicate
You can do this with a type predicate like this:
function isTabWithoutSetIndex(
tabContent: TabWithSetIndex | TabWithoutSetIndex
): tabContent is TabWithoutSetIndex {
return tabContent.length === 0;
}
(The length
property of a function is, roughly speaking, the number of declared formal parameters it has that don't have default values.)
Then, when you're looking at a specific tab.tabContent
, you do:
if (isTabWithoutSetIndex(tab.tabContent)) {
// It's `TabWithoutSetIndex`
tab.tabContent();
} else {
// It's `TabWithSetIndex`
tab.tabContent({x: "foo"});
}
That said, I worry about using the function's length
, since this is a perfectly valid TabWithSetIndex
that has a length
of 0
:
function thisIsATabWithSetIndex(...args[]) {
// ...
}
...since rest parameters don't get count in length
.
A discriminated union (with or without a type predicate)
To avoid using length
, you can use a discriminated union, like this:
type TabWithSetIndex =
((props: TabContentProps) => React.ReactElement) &
{ __type__: "withSetIndex"; };
type TabWithoutSetIndex =
(() => React.ReactElement) &
{ __type__: "withoutSetIndex" };
Then you'd have utility functions to set that __type__
property callers would pass their functions through:
function makeWithSetIndexFunction(
fn: (props: TabContentProps) => React.ReactElement
): TabWithSetIndex {
return Object.assign(fn, {__type__: "withSetIndex"}) as TabWithSetIndex;
}
function makeWithoutSetIndexFunction(
fn: () => React.ReactElement
): TabWithoutSetIndex {
return Object.assign(fn, {__type__: "withoutSetIndex"}) as TabWithoutSetIndex;
}
Then either just test __type__
directly:
if (tab.tabContent.__type__ === "withoutSetIndex") {
// It's `TabWithoutSetIndex`
tab.tabContent();
} else {
// It's `TabWithSetIndex`
tab.tabContent({x: "foo"});
}
...or use a type predicate that's more reliable now:
function isTabWithoutSetIndex(
tabContent: TabWithSetIndex | TabWithoutSetIndex
): tabContent is TabWithoutSetIndex {
return tabContent.__type__ === "withoutSetIndex";
}