2

Suppose I'm writing a class with a method that needs to asynchronously mutate arbitrary properties by their name. The method accepts the name of the property (as a string), as well as an async function that calculates a value to assign to that property.

Provided I'm only passing in keys for the public properties, I can just constrain the property name argument with keyof this and the function argument with an indexed access type on that key. However, that fails for private properties, since keyof this only includes the public ones.

For instance, in this example the second call to initProperty is rejected by the type checker:

class Thing {
  foo: number = 0;
  private bar: number = 0;
  // ...

  private async init(): Promise<void> {
    // Fire off some parallel async requests to initialize our properties
    await Promise.all([
      this.initProperty('foo', someAsyncFn), // Works because foo is public
      this.initProperty('bar', someAsyncFn), // Error because bar is private
      // ...
    ]);

    // Do some other stuff...
  }

  // Initializes this[key] with the resolved value of the Promise returned by fn
  private async initProperty<K extends keyof this>(key: K, fn: () => Promise<this[K]>) {
    return fn().then(result => this[key] = result);
  }

  // ...
}

(Playground link)

While this example is a bit contrived, I'm mostly interested in whether it's even possible to constrain a method so that it accepts keys for both public and private/protected members of a class, without having to manually enumerate all the acceptable keys in a separate union/interface. In other words, how can I make the method accept all strings S for which Thing[S] is a valid type, but still maintain type safety when mutating the corresponding property values?

Octahedron
  • 893
  • 2
  • 16
  • 31
  • jcalz has a good answer on this concept https://stackoverflow.com/a/57078353/9987590. Looks like out of the box it is not possible, but if you are willing to rework the internals of your class to match the structure he outlines, then there is a functionally similar workaround – nate-kumar Feb 04 '23 at 07:39
  • See [ms/TS#13543](https://github.com/microsoft/TypeScript/issues/13543) and [ms/TS#46802](https://github.com/microsoft/TypeScript/issues/46802) – jsejcksn Feb 04 '23 at 10:52

0 Answers0