title: Use Tuple instead of Array of Union within Type
body: <p>Is there a way to more strictly type the following two functions <code>toCsv()</code> and <code>toArray()</code> such that <code>typeof csv</code> is</p>
  
  <pre><code>[["key", "life", "goodbye"], ...[string, number, boolean][]]
  </code></pre>
  
  <p>instead of</p>
  
  <pre><code>[("key" | "life" | "goodbye")[], ...(string | number | boolean)[][]]
  </code></pre>
  
  <p>and <code>typeof original</code> is the same as <code>typeof values</code>, that is</p>
  
  <pre><code>{ key: string, life: number, goodbye: boolean }[]
  </code></pre>
  
  <p>instead of</p>
  
  <pre><code>{ key: any, life: any, goodbye: any }[]
  </code></pre>
  
  <p>I realize that iteration order of <code>{ key: 'value', life: 42, goodbye: false }</code> using <code>for...in</code> is not guaranteed and I'm fine with that. Any consistent order that aligns keys with respective values for each row is acceptable, even if the TypeScript compiler doesn't produce the same order as runtime, since the usage doesn't rely on any particular ordering.</p>
  
  <pre><code>type Key&lt;T&gt; = Extract&lt;keyof T, string&gt;;
  type Column&lt;T&gt; = [Key&lt;T&gt;, ...T[Key&lt;T&gt;][]];
  type Columns&lt;T&gt; = [Key&lt;T&gt;[], ...T[Key&lt;T&gt;][][]];
  
  function toCsv&lt;T&gt; (array: T[]): Columns&lt;T&gt; {
      const columns: Column&lt;T&gt;[] = [];
  
      for (const key in array[0]) {
          columns.push([key, ...array.map(value =&gt; value[key])]);
      }
  
      const keys: Key&lt;T&gt;[] = [];
      const rows: T[Key&lt;T&gt;][][] = array.map(() =&gt; []);
  
      for (const [key, ...values] of columns) {
          keys.push(key);
  
          for (const [index, row] of rows.entries()) {
              row.push(values[index]);
          }
      }
  
      return [keys, ...rows];
  }
  
  function toArray&lt;T&gt; (csv: Columns&lt;T&gt;): T[] {
      const [keys, ...rows] = csv;
  
      return rows.map(
          row =&gt; keys.reduce(
              (o, key, index) =&gt; Object.assign(o, { [key]: row[index] }),
              {} as Partial&lt;T&gt;
          ) as T
      );
  }
  
  const values = [{ key: 'value', life: 42, goodbye: false }];
  const csv = toCsv(values);
  const original = toArray(csv);
  </code></pre>