I have the following types:
type OrBranch = {
or: Branch[]
}
type AndBranch = {
and: Branch[]
}
I'd like a type Branch
that can be either an OrBranch
or an AndBranch
. So I first tried:
type Branch = AndBrand | OrBranch
Work great, unless I want to do something like:
let branch: Branch = ...
let andOr = 'and'; // or 'or'
let nodes = branch[andOr]
Then I get that branch
isn't indexable. OK, so I try an indexable type:
type AndOr = 'and' | 'or';
type Branch = Record<AndOr, Branch[]>;
But that requires that BOTH and
and or
exist, so I can't cast and AndBranch to Branch in this case.
Similarly
type Branch = Record<AndOr, Branch[]> | AndBranch | OrBranch
doesn't work, for the same reason.
While I could use type guards to determine the type, I have long functions that operate on these objects, in which they can be treated the same other than the property. I was hoping to eliminate a bunch of duplicate code by using the andOr
variable, which type guards don't really prevent. For example:
let retval = {} as Branch;
if (isAnd(branch)) { // branch is a Branch parameter passed in
(retval as AndBranch).and = [] as Branch[];
set = (retval as AndBranch).and;
} else {
(retval as OrBranch).or = [] as Branch[];
set = (retval as OrBranch).or;
}
set = _.reduce(set, (all, item: Branch)=> {
if (isAnd(branch) && isAnd(item))
return _.union(all, item.and);
else if (isOr(branch) && isOr(item))
return _.union(all, item.or);
else
return all;
}, [] as Branch[]);
vs.
andOr = isAnd(branch) ? 'and' : 'or';
let retval = {} as Branch;
retval[andOr] = _.reduce(set, (all, item: Branch) => {
if (item[andOr])
return _.union(all, item[andOr]);
else
return all;
}, [] as Branch[]);
I know there's a way to require exactly one of and
and or
(like the answer to Enforce Typescript object has exactly one key from a set). But that type is not indexable.
Is it possible to get both effects?