How can I change the static property field's value in a property decorator in TypeScript?

Viewed 229

I'd like to give the static properties some initial values based on certain calculation if those properties have been decorated so I made such code:

function deco(display?: string) {
  // It looks like `any` could work here instead of `T` but I don't want to use `any`
  return function <T> (target: T, key: string) {
    /*
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
  No index signature with a parameter of type 'string' was found on type '{}'.ts(7053)
    */
    if (typeof target[key] !== 'number') {
      target[key] = 0
    }
  }
}

class A {
    @deco('foo')
    static foo : number

    @deco('bar')
    static bar : number
}

How could I make it work?

1 Answers

Since you don't want to use any, you could try something like this for the factory (inner) function, using additional method type parameters and a type intersection:

return function <T, K extends keyof T> (target: T, key: K) {
  if (typeof target[key] !== 'number') {
    target[key] = 0 as T[K] & number;
  }
}

The issue with this code is that it assumes @deco will only be used on properties of number type, and may have unexpected behavior otherwise. For instance, if you use this decorator on a property of type string, it will still set its value to 0. This is acceptable because TypeScript is just a superset of JavaScript - and so is perfectly valid JavaScript - and we force the setting of target[key] to be the intersection type of T[K] (i.e. the TypeScript-defined type of target[key]; to allow setting target[key] to the value) and number (to allow the number 0 to be assigned). It skirts around type safety.

Ideally, you would be setting initial values directly at their declaration rather than relying on a decorator to populate them.

Related