У меня ниже

const foo = (
  fields: {
    [key: string]: string,
  }
) => {
  const { one, two } = Object.values(fields).reduce(
    (acc, field) => {
      if (isOne(field)) {
        return { ...acc, two: [...acc.two, field] }
      }
      return { ...acc, one: [...acc.one, field] }
    },
    { one: [], two: [] }
  )

  // ...
}

Но я все время получаю сообщение об ошибке

Argument of type '(acc: { one: never[]; two: never[]; }
0
stackjlei 13 Июн 2020 в 02:27

1 ответ

Лучший ответ

Если вы присваиваете переменной объект, содержащий пустые литералы массива, компилятор будет склонять к выводу, что эти массивы имеют тип never[], что является довольно бесполезным типом:

const init = { one: [], two: [] };
/* const init: {
    one: never[];
    two: never[];
} */

init.one.push("whoops"); // error!

Вероятно, это ограничение TypeScript в дизайне согласно microsoft / TypeScript # 29398. Вы можете ожидать, что компилятор отложит присвоение типа пустому литералу массива до тех пор, пока у него не появится больше контекста для того, какими будут типы элементов, но такой отложенный вывод часто имеет другие побочные эффекты.

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

const init2: { one: string[], two: string[] } = { one: [], two: [] };
init2.one.push("okay"); // okay

Для кода reduce(), который у вас здесь, компилятор делает вывод, что аккумулятор имеет тип {one: never[], two: never[]}. Он не может согласовать это с типом возвращаемого значения обратного вызова редуктора (что-то вроде {one: string[], two: never[]} | {one: never[], two: string[]}) и поэтому отказывается от этой сигнатуры вызова. Поскольку reduce() является перегруженной функцией и компилятор не может сопоставить ваш вызов ни одной из сигнатур вызовов, вы получите сообщение об ошибке перегруженных функций.

Опять же, правильнее всего здесь сказать компилятору, что начальное значение вашего аккумулятора имеет тип {one: string[], two: string[]}. Самый простой способ сделать это - явно указать общий параметр типа в вызове reduce():

const { one, two } = Object.values(fields).
  reduce<{ one: string[], two: string[] }>( // <-- specify here
    (acc, field) => {
      if (isOne(field)) {
        return { ...acc, two: [...acc.two, field] }
      }
      return { ...acc, one: [...acc.one, field] }
    },
    { one: [], two: [] }
  )

Теперь ошибки нет: если аккумулятор набирается как {one: string[], two: string[]}, вызов reduce() работает, потому что обратный вызов, как предполагается, возвращает значение того же типа, что и требуется.


Хорошо, надеюсь, что это поможет; удачи!

Детская площадка ссылка на код

2
jcalz 13 Июн 2020 в 01:23