如何正确使用 Array.prototype.reduce 的 TypeScript?

How to use TypeScript with Array.prototype.reduce correct?

我有以下 reduce 函数,无论我尝试什么都没有消除错误:

interface ITask {
  id: string;
  was: string;
}
//sampleData:
const tasks = [
   {id: "a", was: "Foo"}, 
   {id: "b", was: "Foo"}, 
   {id: "c", was: "Bad"}
]; 
const uniqueList = tasks.reduce<>((acc, current) => {
  const x = acc.find((item: ITask) => item.was === current.was);
  return !x ? acc.concat(current) : acc;
}, []);

这给了我:

Property 'find' does not exist on type 'never'.
Property 'was' does not exist on type 'never'.
Property 'concat' does not exist on type 'never'.

current 值是 ITask 类型,accumulatorITask[]|[] 类型,这对我来说是绝对合乎逻辑的。因此,我尝试了:

const uniqueList = tasks.reduce<>((acc: ITask[] | [], current: ITask) => {
  const x = acc.find((item: ITask) => item.was === current.was);
  return !x ? acc.concat(current) : acc;
}, []);

这给出:

Argument of type '(acc: ITask[] | [], current: ITask) => ITask[]' is not assignable to parameter of type '(previousValue: never, currentValue: never, currentIndex: number, array: never[]) => never'.
  Type 'ITask[]' is not assignable to type 'never'.
Argument of type 'ITask' is not assignable to parameter of type 'ConcatArray<never>'.
  Type 'ITask' is missing the following properties from type 'ConcatArray<never>': length, join, slice

编辑:


来自我试过的评论:

const uniqueList = tasks.reduce((acc, current: ITask) => {
  const x = acc.find((item: ITask) => item.was === current.was);
  return !x ? acc.concat(current) : acc;
}, [] as ITask[] | []);

这给了我:

Property 'find' does not exist on type 'never'.
Property 'concat' does not exist on type 'never'.

使用更多类型的指标。看到这个 stackblitz snippet.

const tasks: ITask[] = [
//    ^ note: typo in question
   {id: "a", was: "Foo"},
   {id: "b", was: "Foo"},
   {id: "c", was: "Bad"},
]; 
const uniqueList: ITask[] = tasks.reduce<ITask[]>((acc: ITask[], current: ITask) => {
  const x = acc.find((item: ITask) => item.was === current.was);
  return !x ? acc.concat(current) : acc;
}, []);

仅供参考,这里是一个使用 useState.

在 React 组件中使用的示例

TS Playground

export interface ITask {
  id: string;
  was: string;
}

const tasks: ITask[] = [
  {id: "a", was: "Foo"}, 
  {id: "b", was: "Foo"}, 
  {id: "c", was: "Bad"}
]; 

// 'tasks' is typed by useState
//const [tasks, setTasks] = useState<ITask[]>([]);

const uniqueList = tasks.reduce<ITask[]>((acc, current) => {
  const x = acc.find((item: ITask) => item.was === current.was);
  return !x ? acc.concat(current) : acc;
}, []);

查看完整内容 codesandbox

import { useEffect, useState } from "react";

export interface ITask {
  id: string;
  was: string;
}

interface IProps {
  [tasks: string]: ITask[];
}

export default function App({ tasks: _tasks }: IProps) {
  const [tasks, setTasks] = useState<ITask[]>([]);

  useEffect(() => {
    setTasks(_tasks);
  }, [_tasks]);

  function uneek() {
    const uniqueList = tasks.reduce<ITask[]>((acc, current) => {
      const x = acc.find((item: ITask) => item.was === current.was);
      return !x ? acc.concat(current) : acc;
    }, []);
    setTasks(uniqueList);
  }

  return (
    <div className="App">
      <h1>Tasks</h1>
      {tasks.length
        ? tasks.map(({ id, was }) => (
            <div key={id}>
              <h4>{was}</h4>
            </div>
          ))
        : null}
      <button type="button" onClick={uneek}>
        uneek
      </button>
    </div>
  );
}