Sean
Sean

Reputation: 655

Typescript: extending function argument

I'm wondering if there's a (working) way to extend an implicit argument's type without a new variable,

For example:

;[{ firstName: "John", lastName: "Smith" }]
.map( person => {
  const newPerson = person as typeof person & { name: string }
  newPerson.name = `${newPerson.firstName} ${newPerson.lastName}`
  return newPerson
}

Something like...

.map( (person: person & { name: string } ) => ...

Can I use a generic for this?

Upvotes: 1

Views: 140

Answers (3)

ghybs
ghybs

Reputation: 53185

extend an implicit argument's type without a new variable

No, whether the type was inferred or explicit, TypeScript will consider any extra property as a potential typo.

"Workarounds" are indeed re-typing (type assertion) or making a new variable, which explicitly say to TypeScript that the extra members are intentional.


This may look like a hindrance for seasoned JavaScript developers, for whom freely adding new properties is a very common practice.

This is one of the few situations where TypeScript does force you to code things quite differently than in plain JS.

Here it is more like an annoyance than a critical limitation, as the workaround is quite simple (BTW, in JS you could have used forEach instead, since you "rework" items, whereas map already points semantically to the TS solution, as explained by GuerricP's answer).

You can see it as the price to pay for type explicitness.

Upvotes: 1

Guerric P
Guerric P

Reputation: 31805

What you need is not extending the function argument but use your own return value, which is precisely what Array.prototype.map is meant for. Thus, map is typed like this in TypeScript:

interface Array<T> {
  // ...
  map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
  // ...
}

And U can be inferred from the map callback parameter, so just use TypeScript's inference feature:

const mapped = [{ firstName: "John", lastName: "Smith" }]
  .map((person) => ({ ...person, name: `${person.firstName} ${person.lastName}` }));

type Mapped = typeof mapped; // type Mapped = { name: string; firstName: string; lastName: string; }[]

TypeScript playground

Upvotes: 1

tejastn10
tejastn10

Reputation: 525

You can define a new Type which you can use later on .

type NewPerson = Person & {name: string}

And the best approach I feel would be to return the map with

const newPersons: NewPerson[] = persons.map(person => {
   const {firstName, lastName} = person;
   const newPerson = {...person, name: `${firstName} ${lastName}`};
   return newPerson;
})

The advantage here is that as you have defined the type if anything changes the typescript would through error for the array which was returned by using map.

Upvotes: 1

Related Questions