title: Can you create a Typescript type that has exactly one of a set of properties AND is indexable by that set
body: <p>I have the following types:</p>
  <pre><code>type OrBranch = {
     or: Branch[]
  }
  
  type AndBranch = {
     and: Branch[]
  }
  </code></pre>
  <p>I'd like a type <code>Branch</code> that can be either an <code>OrBranch</code> or an <code>AndBranch</code>.  So I first tried:</p>
  <pre><code>type Branch = AndBrand | OrBranch
  </code></pre>
  <p>Work great, unless I want to do something like:</p>
  <pre><code>let branch: Branch = ...
  let andOr = 'and';   // or 'or'
  
  let nodes = branch[andOr]
  </code></pre>
  <p>Then I get that <code>branch</code> isn't indexable.  OK, so I try an indexable type:</p>
  <pre><code>type AndOr = 'and' | 'or';
  type Branch = Record&lt;AndOr, Branch[]&gt;;
  </code></pre>
  <p>But that requires that BOTH <code>and</code> and <code>or</code> exist, so I can't cast and AndBranch to Branch in this case.</p>
  <p>Similarly</p>
  <pre><code>type Branch = Record&lt;AndOr, Branch[]&gt; | AndBranch | OrBranch
  </code></pre>
  <p>doesn't work, for the same reason.</p>
  <p>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 <code>andOr</code> variable, which type guards don't really prevent.  For example:</p>
  <pre><code>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)=&gt; {
     if (isAnd(branch) &amp;&amp; isAnd(item)) 
        return _.union(all, item.and);
     else if (isOr(branch) &amp;&amp; isOr(item)) 
        return _.union(all, item.or);
     else 
        return all;
  }, [] as Branch[]);
  </code></pre>
  <p>vs.</p>
  <pre><code>andOr = isAnd(branch) ? 'and' : 'or';
  let retval = {} as Branch;
  retval[andOr] = _.reduce(set, (all, item: Branch) =&gt; {
     if (item[andOr]) 
        return _.union(all, item[andOr]);
     else
        return all;
  }, [] as Branch[]);
  </code></pre>
  <p>I know there's a way to require exactly one of <code>and</code> and <code>or</code> (like the answer to <a href="https://stackoverflow.com/questions/60872063/enforce-typescript-object-has-exactly-one-key-from-a-set">Enforce Typescript object has exactly one key from a set</a>).  But that type is not indexable.</p>
  <p>Is it possible to get both effects?</p>