使用元组代替类型内的联合数组

问题描述 投票:1回答:1

是否有一种方法可以更严格地键入以下两个函数toCsv()toArray(),使得typeof csv

[["key", "life", "goodbye"], [string, number, boolean]]

代替

[("key" | "life" | "goodbye")[], ...(string | number | boolean)[]]

并且typeof originaltypeof values相同,而不是

{ key: any, life: any, goodbye: any }[]

我意识到不能保证使用{ key: 'value', life: 42, goodbye: false }进行for...in的迭代顺序,对此我很好。即使TypeScript编译器不会产生与运行时相同的顺序,也可以接受将键与每一行的相应值对齐的任何一致顺序,因为用法不依赖于任何特定顺序。

type Key<T> = Extract<keyof T, string>;
type Column<T> = [Key<T>, ...T[Key<T>][]];
type Columns<T> = [Key<T>[], ...T[Key<T>][][]];

function toCsv<T> (array: T[]): Columns<T> {
    const columns: Column<T>[] = [];

    for (const key in array[0]) {
        columns.push([key, ...array.map(value => value[key])]);
    }

    const keys: Key<T>[] = [];
    const rows: T[Key<T>][][] = array.map(() => []);

    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<T> (csv: Columns<T>): T[] {
    const [keys, ...rows] = csv;

    return rows.map(
        row => keys.reduce(
            (o, key, index) => Object.assign(o, { [key]: row[index] }),
            {} as Partial<T>
        ) as T
    );
}

const values = [{ key: 'value', life: 42, goodbye: false }];
const csv = toCsv(values);
const original = toArray(csv);
typescript reflection types tuples transpose
1个回答
0
投票

我的解决方案有点小巧,但是可以用。

type CSV<T> = {keys: (keyof T)[], values: (T[keyof T])[][], original: T}

const toCsv = <T extends object>(values: T[]): CSV<T> => {
  if(values.length === 0) {
    throw new Error('Values must have length of more than one')
  }
  else {
    return {
      keys: Object.keys(values[0]) as (keyof T)[],
      values: values.map(value => Object.keys(value).map(key => value[key])) as T[keyof T][][],
      original: values[0]
    }  
  }
}

const toArray = <T extends object>(csv: CSV<T>): T[] => {
  return csv.values.map(values => values.reduce<T>((result, value, index) => ({...result, [csv.keys[index]]: value}), {} as T))
}

const values = [{ key: 'value', life: 42, goodbye: false }];
const csv = toCsv(values);
const original = toArray(csv);

type Result = typeof original extends typeof values ? true : never

original的类型将与values相同。您可以通过将光标悬停在Result上来检查自己。

除了类型检查之外,该实现还可以在运行时中运行。

© www.soinside.com 2019 - 2024. All rights reserved.