Reputation: 49
Case #1
I am getting error:
Property 'name' does not exist on type 'T'
function test<T>(arr:Array<T>, item:T) {
const idxReplaced = arr.findIndex(element=> element.name === item.name)
return {
...arr.slice(0, idxReplaced),
...arr.slice(idxReplaced+1)
}
}
interface IItem {
name: number
}
console.log(test<IItem>([{name:1},{name:3}], {name: 3}))
Case #2
I want to change every name property in object array according its name.
I am getting errors:
Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 'ICurrentItem'
and
Property 'name' does not exist on type 'IItem'.
interface IItem {
user: string,
}
interface ICurrentItem {
[key: string]: IItem
}
function test<IItem>(arr:Array<IItem>, item:IItem) {
return arr.reduce((acc:ICurrentItem,el:IItem)=> {
acc[el.user] = item[el.user]
return acc
},{})
}
console.log(test([{user:'info'},{user:'test'}], {user: 'some info'}))
You can test it here: Typescript Playground
Upvotes: 0
Views: 54
Reputation: 42160
@kaya3's comment is correct. Your function assumes that your array elements and your item
variable (both type T
) will have a .name
property. So you must ensure that all possible types for T
include at least a name
.
function test1<T extends { name: any }>(arr: Array<T>, item: T) {
const idxReplaced = arr.findIndex(element => element.name === item.name)
return [
...arr.slice(0, idxReplaced),
...arr.slice(idxReplaced + 1)
]
}
You don't need to set the generic here because it is inferred as <{name: number}>
based on the arguments.
console.log(test1([{ name: 1 }, { name: 3 }], { name: 3 }))
function test<IItem>(arr:Array<IItem>, item:IItem) {
This right here shows a misunderstanding of generic type parameters. You can use any name for the generic type and <IItem>
here is basically <T>
with another name. It is a local type variable that bears no relation to the interface IItem
.
I suspect what you meant to do was something like this, where the generic type must extend {user: string}
:
function test2<T extends IItem>(arr: Array<T>, item: T) {
Or to not have it be generic at all, and simply add types to the arguments:
function test2(arr: Array<IItem>, item: IItem) {
We are building an object acc
where the key is a string
from the item's user
property and the value is an item
. You've got that part typed correctly. It's fine to access acc[el.user]
because acc
has a string index.
But what are you trying to to do with item[el.user]
? el.user
is the username string
and item
is an IItem
. It does not have a property that corresponds to the user
value.
In your example:
console.log(test2([{ user: 'info' }, { user: 'test' }], { user: 'some info' }))
You would be accessing ({ user: 'some info' }).info
and ({ user: 'some info' }).test
. Typescript is right to warn you that those don't exist.
I'm genuinely not sure what you are trying to achieve here. If you meant to turn an array of items into an object keyed by the username then you don't need a second argument item
for that. That would be:
interface IItem {
user: string,
}
interface ICurrentItem<T extends IItem> {
[key: string]: T
}
function test2<T extends IItem>(arr: Array<T>) {
return arr.reduce((acc: ICurrentItem<T>, el: T) => {
acc[el.user] = el
return acc
}, {})
}
console.log(test2([{ user: 'info' }, { user: 'test' }]))
edit: "I want to change every name property in object array according its name."
I think maybe you want map instead of reduce?
This code replaces the user
property of the array elements with the user property from item.user
("some info").
function test<T extends IItem>(arr: Array<T>, item: T) {
return arr.map(el => ({ ...el, user: item.user }));
}
console.log(test([{ user: 'info' }, { user: 'test' }], { user: 'some info' }))
// output: [{ "user": "some info" }, { "user": "some info" }]
Upvotes: 1