Home > Software design >  Typescript: extending function argument
Typescript: extending function argument

Time:01-08

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?

CodePudding user response:

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.

CodePudding user response:

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

CodePudding user response:

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.

  • Related