Свести вложенный массив с выводом типа - PullRequest
2 голосов
/ 20 июня 2019

Я пытаюсь создать функцию, которая может сглаживать вложенные массивы в машинописи.

Пока у меня есть это:

function flattenArrayByKey<T, TProp extends keyof T>(array: T[], prop: TProp): T[TProp] {
    return array.reduce((arr: T[TProp], item: T) => [...arr, ...(item[prop] || [])], []);
}

* * * array.reduce там делает именно то, что я хочукак есть, но я не могу заставить дженерики хорошо сочетаться с тем, что я хочу.Я думаю, что моя проблема в том, что item[prop] возвращает any, поскольку он не может сделать вывод, что item[prop] возвращает T[TProp].

Я стремлюсь к функции, которая может принимать эту структуру:

interface MyInterface {
    arrayProperty: string[];
    anotherArray: number[]
    someNumber: number;
}

const objectsWithNestedProperties: MyInterface[] = [
    {
        arrayProperty: ['hello', 'world'],
        anotherArray: [1, 2],
        someNumber: 1,
    },
    {
        arrayProperty: ['nice', 'to'],
        anotherArray: [3, 4],
        someNumber: 2,
    },
    {
        arrayProperty: ['meet', 'you'],
        anotherArray: [5, 6],
        someNumber: 3,
    },
];

и вернуть массив, содержащий содержимое всего вложенного массива.

const result = flattenArrayByKey(objectsWithNestedProperties, 'arrayProperty');

result должен выглядеть как ['hello', 'world', 'nice', 'to', 'meet', 'you']

По сути, яищем SelectMany из C # linq.

Ответы [ 2 ]

1 голос
/ 20 июня 2019

Примечание. Следующий ответ был протестирован на TS3.5 в режиме --strict.Ваш пробег может отличаться, если вы используете другие версии или флаги компилятора.


Как насчет этого:

function flattenArrayByKey<K extends keyof any, V>(array: Record<K, V[]>[], prop: K): V[] {
    return array.reduce((arr, item) => [...arr, ...(item[prop] || [])], [] as V[]);
}

Вы должны сообщить компилятору, что T[TProp] будетмассив.Вместо того, чтобы пытаться пойти по этому пути, у меня есть общие обозначения: K (который вы называли TProp) и V, тип элемента свойства массива в array[number][K].Затем вы можете ввести array как Record<K, V[]>[] вместо T[] (A Record<K, V[]> - это объект, свойство которого по ключу K имеет тип V[]).И это возвращает V[].

Теперь компилятор понимает, что вы пытаетесь сделать, хотя вам нужно сказать ему, что исходный пустой массив в качестве второго параметра для reduce должен быть V[] (следовательно, [] as V[]).

И это должно работать так, как вы хотите.Надеюсь, это поможет;удачи!

Ссылка на код

Обновление: вышеупомянутое, кажется, не очень хорошо подходит, когда объект имеет массивы различных типов.Возможно, вам придется вводить его явно, например, flattenArrayByKey<"anotherArray", number>(objectsWithNestedProperties, "anotherArray"), что является избыточным и раздражающим.

Ниже приведена более сложная подпись, но она имеет лучший вывод и лучшие предложения IntelliSense:

type ArrayKeys<T> = { [K in keyof T]: T[K] extends any[] ? K : never }[keyof T];

function flattenArrayByKey<T extends Record<K, any[]>, K extends ArrayKeys<T>>(
  array: T[],
  prop: K
): T[K][number][] {
  return array.reduce(
    (arr, item) => [...arr, ...(item[prop] || [])],
    [] as T[K][number][]
  );
}

Он должен вести себя одинаково с точки зрения входов и выходов, но если вы начнете набирать

const result = flattenArrayByKey(objectsWithNestedProperties, "");
// put cursor here and get intellisense prompts (ctrl-spc) ---^

Он предложит "arrayProperty" (а теперь "anotherArray") в качестве второго параметра, поскольку только arrayProperty"anotherArray") подходит для такого сокращения.

Надеюсь, это поможет снова.Удачи!

Ссылка на код

0 голосов
/ 24 июня 2019

Оказывается, ESNext имеет Array.flatMap, что делает именно то, что я хочу.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap

Синтаксис

var new_array = arr.flatMap(function callback(currentValue[, index[, array]]) {
   // return element for new_array
}[, thisArg])

callback Функция, которая создает элемент нового массива, принимая три аргумента:

currentValue Текущий элемент, обрабатываемый в массиве.

index (необязательно) Индекс текущего элемента, обрабатываемого в массиве.

array (необязательно) Была вызвана карта массива.

thisArg (необязательно) Значение для использования в качестве this при выполнении обратного вызова.

Чтобы использовать его, мне нужно было добавить esnext.array к моему lib в tsconfig.json:

{
    "compilerOptions": {
         "lib": ["es2018", "dom", "esnext.array"]
     }
}

Это именно то, что я хочу:

objectsWithNestedProperties.flatMap(obj => obj.arrayProperty)
// returns ['hello', 'world', 'nice', 'to', 'meet', 'you']

NB : Это не поддерживается IE, Edge и Samsung Internet.

...