Home > Net >  Return an instance of union type after spreading it
Return an instance of union type after spreading it

Time:06-06

I have a general interface which I use to define properties (let's say it's an Animal).
Then I have extensions of this interface, which have additional different fields.

I want to define a function that gets an instance of an animal, spreads it - and parses a specific field.
The result value should be the same as the input's; however I get an error, as not all animals are equal :P

How can I use the spread operator and still return the same type?

interface AnimalProps { name: string; size: string; }

class Dog implements AnimalProps {
  name: string;
  size: string;
  bark(): void { console.log(`${this.name} says: 'woof'`); }
  constructor(name: string, size: string) {
    this.name = name;
    this.size = size;
  }
}

class Bird implements AnimalProps {
  name: string;
  size: string;
  fly(): void  { console.log(`${this.name} says: 'I love flying'`); }
  constructor(name: string, size: string) {
    this.name = name;
    this.size = size;
  }
}

type Animal = Dog | Bird;
function addSizeUnits(animal: Animal): Animal {
  // Property 'fly' is missing in type '{ size: string; name: string; }' but required in type 'Bird'.
  return { ...animal, size: `${animal.size} meters` };
}

Obviously I'm simplifying the existing (legacy) code here, but the bottom line is that I do need to use fly and bark and therefore I need the actual union types - and not the basic Animal.

CodePudding user response:

A generic function should work here. Something like:

function addSizeUnits<T extends Animal>(animal: T) {
  return { ...animal, size: `${animal.size} meters` };
}

Note that I removed the return type annotation because the type inference should figure that out for you, but if you want to add it back you can use something like T & { size: string } correction, just T should be fine because you already have size in AnimalProps.

CodePudding user response:

This can easily be addressed by using a generic constraint:

function addSizeUnits<A extends Animal>(animal: A): A {
  return { ...animal, size: `${animal.size} meters` };
}

This ensures that your argument type and your return type are the same type of Animal.

  • Related