Home > Back-end >  Typescript: Shortening Simple Class Declaration
Typescript: Shortening Simple Class Declaration

Time:12-12

I have a TS code pattern that I find very useful but it is extremely not DRY. Any ideas how to fix it?

I like to "link" together a TS interface with a class declaration. In this way I have the advantage of some simple typed data-structure with inheritance, AND I can easily do type checks at runtime with a instanceof operator (that allows me to avoid type predicates, which I find unsafe).

For an example see the code below, where I have a class Doctor which extends a base class Person

interface PersonInterface {
  id: number
  name: string
  surname: string
}
class Person implements PersonInterface {
  id: number
  name: string
  surname: string

  constructor(arg: PersonInterface) {
    this.id = arg.id
    this.name = arg.name
    this.surname = arg.surname
  }
}

interface DoctorInterface extends PersonInterface {
  degree: string
  salary: number
}
class Doctor extends Person implements DoctorInterface {
  degree: string
  salary: number

  constructor(arg: DoctorInterface) {
    super(arg)
    this.degree = arg.degree
    this.salary = arg.salary
  }
}

const doc = new Doctor({
  id: 111,
  name: 'John',
  surname: 'Johnson',
  degree: 'PHD',
  salary: 100000,
})

console.log(doc instanceof Person) // true
console.log(doc instanceof Doctor) // true

Everything works, the type check is easy and my IntelliSense is happy. All great. But as you can see I am repeating myself 3 times for each class. One time to declare the interface, another to implement it in the class and a final one to apply the constructor.

Isn't there a more coincise way? In a big project this becomes horrible to look at.

IMPORTANT I do not need to have methods in my classes. They are used only to rapresent data, not behaviour (in fact I am using them to populate a vuex store)

CodePudding user response:

First of all, there is no magic syntax to make this nicer.

But it is worth noting that a class can be used as an interface:

class Person {
  id: number
  name: string
  surname: string

  constructor(arg: Person) {
    this.id = arg.id
    this.name = arg.name
    this.surname = arg.surname
  }
}

// works
const personData: Person = { id: 123, name: 'Al', surname: 'Bundy' }
const person: Person = new Person(personData)

You may think that is a bit odd, but the plain object and the instance of Person both have the exact same public interface, so they are considered compatible.


IMPORTANT I do not need to have methods in my classes. They are used only to rapresent data, not behaviour (in fact I am using them to populate a vuex store)

So why are you using classes at all? Then there's no need. A plain object that conforms to that interface is more or less indistinguishable from the instance of a class that implements that interface, so you can save a lot of boilerplate with just:

interface Person {
  id: number
  name: string
  surname: string
}

const person: Person = { id: 123, name: 'Al', surname: 'Bundy' }
  • Related