Learning typescript. Here is the code:
interface TodoI {
id: number;
title: string;
status: string;
completedOn?: Date;
}
const todoItems: TodoI[] = [
{ id: 1, title: "Learn HTML", status: "done", completedOn: new Date("2021-09-11") },
{ id: 2, title: "Learn TypeScript", status: "in-progress" },
{ id: 3, title: "Write the best app in the world", status: "todo", },
]
function addTodoItem(todo: string): TodoI {
const id = getNextId<TodoI>(todoItems)
const newTodo = {
id,
title: todo,
status: "todo",
}
todoItems.push(newTodo)
return newTodo
}
function getNextId<T>(items: T[]) : number {
// following line is causing the issue
// Property 'id' does not exist on type 'T'.ts(2339)
return items.reduce((max, x) => x.id > max ? x.id : max, 0) + 1
}
const newTodo = addTodoItem("Buy lots of stuff with all the money we make from the app")
console.log(JSON.stringify(newTodo))
While calling the getNextId()
function I’ve specified type type TodoI
, then why I am supposed to extend the generic type?
2
Answers
The problem here is not with the function call.
So if you remove the function call but not the definition you should in theory get the same error (assuming this error comes up before the tree-shaking phase in the bundling process).
Now to answer you question about extending the generic with
TodoI
. You don’t necessarily have to extend the generic withTodoI
but what you must extend it with is the type{id: number}
. Another way of thinking about this is thatTodoI
is an extension of{id: number}
.Unlike languages such as C++ where templates (which are equivalent of generics in TS) in the function calls are accounted for, during the compile time creating multiple executable copies of the function, TS to JS transpilation does not create multiple copies of the same function. And so when you extend a generic it should account for all the properties that are necessarily expected to be on any type used in place of that generic.
Where you’ve defined
getNextId
, the generic typeT
argument has no properties, equivalent toany
. Your function cannot know thatT
has anid
property.The simplest solution is to enforce the generic type
T
extends a type with anid
propertyFYI because the type passed to your function is inferred by the argument, you don’t need to use generics when calling it