Search code examples
typescriptenumsconstructorimplementsincompatibility

Assigning Enum to Field of Implemented TypeScript Class


Let's say we have an enum, "Profession":

enum Profession {
  Teacher = "Teacher",
  Scientist = "Scientist",
  Rapper = "Rapper",
}

And we have a Person interface, which accepts a generic drawn from Profession's values:

interface Person<P extends Profession> {
  profession: P;
}

And finally, we want to implement Person:

class AmericanCitizen implements Person<Profession.Teacher> {
  // ... problem continued below
}

... within the class implementation, I'd like to assign the generic-specified profession like so:

class AmericanCitizen implements Person<Profession.Teacher> {
  profession = Profession.Teacher;
}

This results in the following TS error:

Property 'profession' in type 'AmericanCitizen' is not assignable to the same property in base type 'Person<Profession.Teacher>'.
  Type 'Profession' is not assignable to type 'Profession.Teacher'.

Playground Link

The compiler forces me to do the long-hand equivalent:

class AmericanCitizen implements Person<Profession.Teacher> {
  profession: Profession.Teacher;

  constructor() {
    this.profession = Profession.Teacher;
  }
}

Playground Link

Why is it that the former is invalid?


Solution

  • class AmericanCitizen implements Person<Profession.Teacher> {
      profession = Profession.Teacher;
    }
    

    Since you haven't told typescript what type you want profession to be, typescript will try to infer it from how you're using it. It sees you're assigning Profession.Teacher to it, which is part of an enum so it assumes you want profession to be that enum. Not a specific value of the enum though; the whole thing. Similarly if you tried to define name = "bob" it would assume you wanted string as the type, not specifically "bob", and age = 100 would be number, not specifically 100.

    You can tell typescript that you want something more specific in several ways:

    profession: Profession.Teacher = Profession.Teacher;
    
    profession = Profession.Teacher as Profession.Teacher;
    
    profession = Profession.Teacher as const;
    

    Your version with a constructor works too and is equivalent to my first example.