英文:
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: "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>
))}
答案1
得分: 1
以下是翻译好的部分:
- 描述任务对象的类型。
interface Task {
id: string;
jobName: string;
created: string;
modified: string;
}
- 为
data
状态设置类型。
const [data, setData] = useState<Task[]>([])
- 将您的
types
对象定义为从string
到Task
键的映射。
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> = {
- 删除
AnotherStringIndexedObject
类型。
排序后的数组不是对象,而是数组Task[]
。您可以写const sorted: Task[]
,但不需要。
- 删除了
new Date
处理部分。
如果排序类型是'jobName'
或'id'
,那么这是没有意义的。对于那些确实是日期的字段,我们可以将ISO字符串视为字符串进行比较。
- 使用lodash的
sortBy
进行排序。
您可以定义一个自定义比较函数,但我觉得这种方式更方便:
const sorted = sortBy(tasks, sortProperty);
- 将数据文件从
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:
- The current sort property.
- 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<Task[]>([])
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) => {
interface StringIndexedObject {
[key: string]: keyof Task
}
const types: StringIndexedObject = {
jobName: 'jobName',
created: 'created',
modified: 'modified'
}
const sortProperty = types[type];
You can also do:
const types: Record<string, keyof Task> = {
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 'jobName'
or 'id'
. What is new Date('MK-005478 • Spacecraft')
? 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 'react';
import { sortBy } from 'lodash';
import { useRouter } from 'next/router';
import { tasks } from './data';
export interface Task {
id: string;
jobName: string;
created: string;
modified: string;
}
export const Job = () => {
const router = useRouter();
const [data, setData] = useState<Task[]>([])
const [sortType, setSortType] = useState('jobName')
useEffect(() => {
const sortArray = (type: string) => {
interface StringIndexedObject {
[key: string]: keyof Task
}
const types: StringIndexedObject = {
jobName: 'jobName',
created: 'created',
modified: 'modified'
}
const sortProperty = types[type];
const sorted = sortBy(tasks, sortProperty);
console.log(sorted)
setData(sorted)
}
sortArray(sortType)
}, [sortType]);
return (
<div>
<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>
</tr>
))}
</div>
)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论