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>
))}
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:
(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 auseState
and auseEffect
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.
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 typenever[]
. If the initial array was not empty then it could be inferred automatically.3. Defined your
types
object as mapping fromstring
tokeyof Task
.Now the
sortProperty
has the typekeyof Task
, meaning that it is always a property of theTask
.You can also do:
4. Removed the
AnotherStringIndexedObject
type.The sorted array is not an object. It is an array
Task[]
. You can writeconst 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 isnew 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.
7. Renamed your data file from
data.js
todata.ts
If it is a TypeScript file then your imported
tasks
variable will have proper types based on its values.Job.tsx