Rfa dada
Rfa dada

Reputation: 49

Problems typing functions using typescript generics

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

Answers (1)

Linda Paiste
Linda Paiste

Reputation: 42160

Case #1

@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 }))

Case #2

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' }]))

Typescript Playground Link

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

Related Questions