Typescript index signature error : Type error: Argument of type is not assignable to parameter of type 'SetStateAction<never[]>'

huangapple go评论83阅读模式
英文:

Typescript index signature error : Type error: Argument of type is not assignable to parameter of type 'SetStateAction<never[]>'

问题

I'm new to typescript and to Index signatures and I don't find how to solve that error in my code. I assume that I need to something like on variable sortProperty on the variable sorted, but I can't find the answer.
I have 2 errors. One on the setData(sorted)

Argument of type { id: number; jobName: string; created: string; modified: string; }[]' is not assignable to parameter of type 'SetStateAction<never[]>'

and the other on (b[sortProperty])

Element implicitly has an 'any' type because expression of type 'string' can't be used to index

data.js

export const tasks = [
    {
      id: "1",
      jobName: 'MK-00544 • Spacecraft',
      created: '2016-09-03',
      modified: '2016-09-04'
    },
    {
      id: "2",
      jobName: 'MK-00545 • Spacecraft',
      created: '2017-09-26',
      modified: '2018-09-29'
    },
    {
      id: "3",
      jobName: 'MK-00546 • Spacecraft',
      created: '2019-10-03',
      modified: '2022-12-08'
    },
    {
      id: "4",
      jobName: 'MK-00547 • Spacecraft',
      created: '2020-09-12',
      modified: '2021-09-03'
    },
    {
      id: "5",
      jobName: 'MK-005478 • Spacecraft',
      created: '2019-09-03',
      modified: '2019-09-03'
    },
    {
      id: "6",
      jobName: 'MK-00549 • Spacecraft',
      created: '2019-09-03',
      modified: '2019-09-03'
    }
  ]

Job.tsx :

 interface Task {
    [key: string]: string
  }
  const [data, setData] = useState<Task>([])
  const [sortType, setSortType] = useState('jobName')

  useEffect(() => {
    const sortArray = (type: string) => {
      interface StringIndexedObject {
        [key: string]: string
      }
      const types: StringIndexedObject = {
        jobName: 'jobName',
        created: 'created',
        modified: 'modified'
      }
      interface AnotherStringIndexedObject {
    [key: string]: keyof Task
  }
  const sortProperty = types[type]
  const sorted: AnotherStringIndexedObject = [...tasks].sort(
    (a, b) => new Date(b[sortProperty]) - new Date(a[sortProperty])
  )
      console.log(sorted)
      setData(sorted)
    }

    sortArray(sortType)
  }, [sortType])
  return (
<div className="font-bold">Sort by:</div>
            <select
              onChange={(e) => setSortType(e.target.value)}
              className="text-center px-5 bg-gray-200 rounded-xl"
            >
              <option value="id">Id</option>
              <option value="jobName">Job name</option>
              <option value="created">Newest</option>
              <option value="modified">Last modified</option>
            </select>
            <div>({tasks.length} users)</div>

   {data.map((task) => (
                <tr
                  key={task.id}
                  className="tableau text-center cursor-pointer"
                  onClick={() => router.push(`/job/${task.id}`)}
                >
                  <td className="py-3">{task.jobName}</td>
                  <td className="py-3">
                    <div className="flex items-center gap-5 justify-center">
                      {task.created}
                    </div>
                  </td>
                  <td className="py-3">
                    <div className="flex items-center gap-5 justify-center">
                      {task.modified}
                    </div>
                  </td>
              ))}
英文:

I'm new to typescript and to Index signatures and I don't find how to solve that error in my code. I assume that I need to something like on variable sortProperty on the variable sorted, but I can't find the answer.
I have 2 errors. One on the setData(sorted)

> Argument of type { id: number; jobName: string; created: string;
> modified: string; }[]' is not assignable to parameter of type
> 'SetStateAction<never[]>'
>
and the other on (b[sortProperty])
> Element implicitly has an 'any' type because expression of type
> 'string' can't be used to index

data.js

export const tasks = [
    {
      id: &quot;1&quot;,
      jobName: &#39;MK-00544 • Spacecraft&#39;,
      created: &#39;2016-09-03&#39;,
      modified: &#39;2016-09-04&#39;
    },
    {
      id: &quot;2&quot;,
      jobName: &#39;MK-00545 • Spacecraft&#39;,
      created: &#39;2017-09-26&#39;,
      modified: &#39;2018-09-29&#39;
    },
    {
      id: &quot;3&quot;,
      jobName: &#39;MK-00546 • Spacecraft&#39;,
      created: &#39;2019-10-03&#39;,
      modified: &#39;2022-12-08&#39;
    },
    {
      id: &quot;4&quot;,
      jobName: &#39;MK-00547 • Spacecraft&#39;,
      created: &#39;2020-09-12&#39;,
      modified: &#39;2021-09-03&#39;
    },
    {
      id: &quot;5&quot;,
      jobName: &#39;MK-005478 • Spacecraft&#39;,
      created: &#39;2019-09-03&#39;,
      modified: &#39;2019-09-03&#39;
    },
    {
      id: &quot;6&quot;,
      jobName: &#39;MK-00549 • Spacecraft&#39;,
      created: &#39;2019-09-03&#39;,
      modified: &#39;2019-09-03&#39;
    }
  ]

Job.tsx :

 interface Task {
    [key: string]: string
  }
  const [data, setData] = useState&lt;Task&gt;([])
  const [sortType, setSortType] = useState(&#39;jobName&#39;)

  useEffect(() =&gt; {
    const sortArray = (type: string) =&gt; {
      interface StringIndexedObject {
        [key: string]: string
      }
      const types: StringIndexedObject = {
        jobName: &#39;jobName&#39;,
        created: &#39;created&#39;,
        modified: &#39;modified&#39;
      }
      interface AnotherStringIndexedObject {
    [key: string]: keyof Task
  }
  const sortProperty = types[type]
  const sorted: AnotherStringIndexedObject = [...tasks].sort(
    (a, b) =&gt; new Date(b[sortProperty]) - new Date(a[sortProperty])
  )
      console.log(sorted)
      setData(sorted)
    }

    sortArray(sortType)
  }, [sortType])
  return (
&lt;div className=&quot;font-bold&quot;&gt;Sort by:&lt;/div&gt;
            &lt;select
              onChange={(e) =&gt; setSortType(e.target.value)}
              className=&quot;text-center px-5 bg-gray-200 rounded-xl&quot;
            &gt;
              &lt;option value=&quot;id&quot;&gt;Id&lt;/option&gt;
              &lt;option value=&quot;jobName&quot;&gt;Job name&lt;/option&gt;
              &lt;option value=&quot;created&quot;&gt;Newest&lt;/option&gt;
              &lt;option value=&quot;modified&quot;&gt;Last modified&lt;/option&gt;
            &lt;/select&gt;
            &lt;div&gt;({tasks.length} users)&lt;/div&gt;

   {data.map((task) =&gt; (
                &lt;tr
                  key={task.id}
                  className=&quot;tableau text-center cursor-pointer&quot;
                  onClick={() =&gt; router.push(`/job/${task.id}`)}
                &gt;
                  &lt;td className=&quot;py-3&quot;&gt;{task.jobName}&lt;/td&gt;
                  &lt;td className=&quot;py-3&quot;&gt;
                    &lt;div className=&quot;flex items-center gap-5 justify-center&quot;&gt;
                      {task.created}
                    &lt;/div&gt;
                  &lt;/td&gt;
                  &lt;td className=&quot;py-3&quot;&gt;
                    &lt;div className=&quot;flex items-center gap-5 justify-center&quot;&gt;
                      {task.modified}
                    &lt;/div&gt;
                  &lt;/td&gt;
              ))}

答案1

得分: 1

以下是翻译好的部分:

  1. 描述任务对象的类型。
interface Task {
    id: string;
    jobName: string;
    created: string;
    modified: string;
}
  1. data状态设置类型。
const [data, setData] = useState<Task[]>([])
  1. 将您的types对象定义为从stringTask键的映射。
const sortArray = (type: string) => {
    interface StringIndexedObject {
        [key: string]: keyof Task;
    }
    const types: StringIndexedObject = {
        jobName: 'jobName',
        created: 'created',
        modified: 'modified'
    };
    const sortProperty = types[type];

或者也可以这样写:

const types: Record<string, keyof Task> = {
  1. 删除AnotherStringIndexedObject类型。

排序后的数组不是对象,而是数组Task[]。您可以写const sorted: Task[],但不需要。

  1. 删除了new Date处理部分。

如果排序类型是'jobName''id',那么这是没有意义的。对于那些确实是日期的字段,我们可以将ISO字符串视为字符串进行比较。

  1. 使用lodash的sortBy进行排序。

您可以定义一个自定义比较函数,但我觉得这种方式更方便:

const sorted = sortBy(tasks, sortProperty);
  1. 将数据文件从data.js重命名为data.ts

如果它是一个TypeScript文件,那么导入的tasks变量将基于其值具有适当的类型。

希望这些部分对您有所帮助。

英文:

You've got a bunch of issues here which are all related. When the type for one variable is incomplete or incorrect it can cause issues in other places where you use that variable.

Your component's job is to sort and render an array of task objects. It internally stores two states:

  1. The current sort property.
  2. The array of sorted tasks.

(Note: the sorted array one doesn't need to be a state as it is a derived value. Personally I would use a useMemo instead of a useState and a useEffect but what you have will work so let's keep it and focus on the TypeScript issues)

Here are the things that I've fixed:

1. Describe the type of a task object.

Not every string is a valid key here. We have a few known properties and that's it.

interface Task {
    id: string;
    jobName: string;
    created: string;
    modified: string;
}

2. Set a type for the data state.

It is an array of tasks aka Task[]. You need a type here because the initial value is an empty array which has type never[]. If the initial array was not empty then it could be inferred automatically.

const [data, setData] = useState&lt;Task[]&gt;([])

3. Defined your types object as mapping from string to keyof Task.

Now the sortProperty has the type keyof Task, meaning that it is always a property of the Task.

const sortArray = (type: string) =&gt; {
    interface StringIndexedObject {
        [key: string]: keyof Task
    }
    const types: StringIndexedObject = {
        jobName: &#39;jobName&#39;,
        created: &#39;created&#39;,
        modified: &#39;modified&#39;
    }
    const sortProperty = types[type];

You can also do:

const types: Record&lt;string, keyof Task&gt; = {

4. Removed the AnotherStringIndexedObject type.

The sorted array is not an object. It is an array Task[]. You can write const sorted: Task[], but you don't need to.

5. Removed the new Date handling.

This doesn't make sense if the sort type is &#39;jobName&#39; or &#39;id&#39;. What is new Date(&#39;MK-005478 • Spacecraft&#39;)? For the fields which are dates, we can compare the ISO strings as strings.

6. Used lodash sortBy for the sorting.

You can define a custom compare function but I find this really annoying.

const sorted = sortBy(tasks, sortProperty);

7. Renamed your data file from data.js to data.ts

If it is a TypeScript file then your imported tasks variable will have proper types based on its values.


Job.tsx

import React, { useState, useEffect } from &#39;react&#39;;
import { sortBy } from &#39;lodash&#39;;
import { useRouter } from &#39;next/router&#39;;
import { tasks } from &#39;./data&#39;;

export interface Task {
    id: string;
    jobName: string;
    created: string;
    modified: string;
}

export const Job = () =&gt; {
    const router = useRouter();

    const [data, setData] = useState&lt;Task[]&gt;([])
    const [sortType, setSortType] = useState(&#39;jobName&#39;)

    useEffect(() =&gt; {
        const sortArray = (type: string) =&gt; {
            interface StringIndexedObject {
                [key: string]: keyof Task
            }
            const types: StringIndexedObject = {
                jobName: &#39;jobName&#39;,
                created: &#39;created&#39;,
                modified: &#39;modified&#39;
            }
            const sortProperty = types[type];
            const sorted = sortBy(tasks, sortProperty);
            console.log(sorted)
            setData(sorted)
        }

        sortArray(sortType)
    }, [sortType]);

    return (
        &lt;div&gt;
            &lt;div className=&quot;font-bold&quot;&gt;Sort by:&lt;/div&gt;
            &lt;select
                onChange={(e) =&gt; setSortType(e.target.value)}
                className=&quot;text-center px-5 bg-gray-200 rounded-xl&quot;
            &gt;
                &lt;option value=&quot;id&quot;&gt;Id&lt;/option&gt;
                &lt;option value=&quot;jobName&quot;&gt;Job name&lt;/option&gt;
                &lt;option value=&quot;created&quot;&gt;Newest&lt;/option&gt;
                &lt;option value=&quot;modified&quot;&gt;Last modified&lt;/option&gt;
            &lt;/select&gt;
            &lt;div&gt;({tasks.length} users)&lt;/div&gt;

            {data.map((task) =&gt; (
                &lt;tr
                    key={task.id}
                    className=&quot;tableau text-center cursor-pointer&quot;
                    onClick={() =&gt; router.push(`/job/${task.id}`)}
                &gt;
                    &lt;td className=&quot;py-3&quot;&gt;{task.jobName}&lt;/td&gt;
                    &lt;td className=&quot;py-3&quot;&gt;
                        &lt;div className=&quot;flex items-center gap-5 justify-center&quot;&gt;
                            {task.created}
                        &lt;/div&gt;
                    &lt;/td&gt;
                    &lt;td className=&quot;py-3&quot;&gt;
                        &lt;div className=&quot;flex items-center gap-5 justify-center&quot;&gt;
                            {task.modified}
                        &lt;/div&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            ))}
        &lt;/div&gt;
    )
}

huangapple
  • 本文由 发表于 2023年1月9日 04:55:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75051224.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定