verbose class definition in TypeScript

64 Views Asked by At

I have a class with a destructured constructor parameter:

import * as ē from 'three'

export class cylindricCurve extends ē.Curve {
   #scale: number
   #eqs: ((θ: number) => number)[]
   #rounds: number
   #mainAxis: 'x' | 'y'
   constructor({
      eqs,
      scale = 1,
      rounds = 1,
      mainAxis = null,
   }: {
      eqs: ((θ: number) => number)[]
      scale: number
      rounds?: number
      mainAxis?: 'x' | 'y'
   }) {
      super()
      this.#scale = scale
      this.#eqs = eqs
      this.#rounds = rounds
      this.#mainAxis = mainAxis
   }

As you can see, this is incredibly verbose, with every name mentioned FIVE times just to initialize member variables. I wonder if there is a more concise way to achieve the task.

2

There are 2 best solutions below

0
T.J. Crowder On

It's hard to prove a negative, but while you can get rid of one of them by not destructuring, I think you're stuck with the other ones because you can't auto-declare-and-initialize a private field from a constructor parameter (like you can with TypeScript's public, protected, and private parameter annotations; and even those you'd have to have discrete parameters for rather than destructuring).

Here's that minor change, just for completeness:

import * as ē from 'three'

export class cylindricCurve extends ē.Curve {
   #scale: number
   #eqs: ((θ: number) => number)[]
   #rounds: number
   #mainAxis: 'x' | 'y'
   constructor(props: {                         // ***
      eqs: ((θ: number) => number)[]
      scale: number
      rounds?: number
      mainAxis?: 'x' | 'y'
   }) {
      super()
      this.#scale = props.scale ?? 1            // ***
      this.#eqs = props.eqs                     // ***
      this.#rounds = props.rounds ?? 1          // ***
      this.#mainAxis = props.mainAxis ?? null   // ***
   }
}
4
punund On

After finding an eight years old issue in TypeScript (https://github.com/Microsoft/TypeScript/issues/5326), I tried to follow one of the workarounds:

import * as ē from 'three'

export class cylindricCurve extends ē.Curve {
   private scale: number = 1
   private eqs: ((a: number) => number)[]
   private rounds?: number = 1
   private mainAxis?: 'x' | 'y' = null

   constructor(_: cylindricCurve) {
      super()
      Object.assign(this, _)
   }
}

While this works, I am not too happy with this approach, which looks unnecessarily hacky, and lacks flexibility: every property now is doomed to be a valid constructor argument.