Home > Software engineering >  Type inference error when typescript generics and class inheritance are used together
Type inference error when typescript generics and class inheritance are used together

Time:09-08

class Animal<T extends string> {
  eat(food: T): void {
    console.log('eating '   food)
  }
}

class Rabbit extends Animal<'carrot'> {}

class Cat extends Animal<'mouse'>{}

type MyPet = Rabbit | Cat
const myPets: Record<string, MyPet> = {
  'aa': new Rabbit(),
  'bb': new Cat()
}

function getPets (name: string): MyPet {
  return myPets[name]
}

const pet = getPets('aa')


// error: Argument of type 'string' is not assignable to parameter of type 'never'.
pet.eat('carrot')
pet.eat('mouse')

Please check the source code [source code replication], The generic method is called by using the joint type of two subclasses. The parameter type is inferred as never. Shouldn't it be carrot | mouse

CodePudding user response:

The problem is that your code doesn't allow TS to work properly by being not precise enough with some of the type definitions. This code works as expected:

class Animal<T extends string> {
  eat(food: T): void {
    console.log('eating '   food)
  }
}

class Rabbit extends Animal<'carrot'> {}

class Cat extends Animal<'mouse'>{}

type MyPet = Rabbit | Cat
const myPets = {
  'aa': new Rabbit(),
  'bb': new Cat()
}

function getPets <T extends keyof typeof myPets>(name: T): typeof myPets[T] {
  return myPets[name]
}

const pet = getPets('aa')

pet.eat('carrot')
pet.eat('mouse') // error: Argument of type '"mouse"' is not assignable to parameter of type '"carrot"'

See here

In your original code you were using

const myPets: Record<string, MyPet> = {
  'aa': new Rabbit(),
  'bb': new Cat()
}

So TS will lose the information that myPets.aa is a Rabbit. It will trat myPets.aa as type MyPet. And since the types 'mouse' and 'carrot' have nothing in common, you cannot feed that pet.

CodePudding user response:

class Animal<T extends string> {
  eat(food: T): void {
    console.log('eating '   food)
  }
}

class Rabbit extends Animal<'carrot'> {}

class Cat extends Animal<'mouse'>{}

// type MyPet = Rabbit | Cat
const myPets = {
  'aa': new Rabbit(),
  'bb': new Cat()
}

function getPets <T extends keyof typeof myPets>(name: T): typeof myPets[T] {
  return myPets[name]
}

// Neme is not sure
const name = 'aa' as keyof typeof myPets

const pet = getPets(name)

// error: Argument of type 'string' is not assignable to parameter of type 'never'.
pet.eat('carrot')
pet.eat('mouse')

See here

  • Related