Typescript dynamic class methods

Problem

How can you add type checking to a dynamically generated class method?

Example

Given a very simple class Property

.

class Property {
  value: any;
  name: string;

  constructor(name: string, value: any) {
    this.name = name;
    this.value = value
  }
}

      

and Entity

class

class Entity {
  name: string;
  properties: Property[];


  constructor(name: string, properties: Property[]) {
    this.name = name;
    this.properties = properties;

    this.properties.forEach((p: Property, index: number) => {
      this[p.name] = (value: string): any => {
        if (value) {
          this.properties[index].value = value;
        }
        return this.properties[index].value;
      }
    }, this);
  }
}

      

The important part: this[p.name] = function ...

(we don't know the name of the method at the time of "forwarding").

When uploading to javascript, we get the following error:

var car = new domain.Entity(
  'car',
  [
    new domain.Property('manufacturer', 'Ford'),
    new domain.Property('model', 'Focus')
  ]
);

car.model() // error TS2339: Property 'model' does not exist on type 'Entity'.

      

I know this is an unusual use of classes as different instances Entity

will have different methods. Is there a way to get rid of the error i.e. typescript is able to identify the correct interface for each instance, or at least disable the error ?

Notes

This is valid javascript and can be used like this:

var car = new domain.Entity(
  'car',
  [
    new domain.Property('manufacturer', 'Ford'),
    new domain.Property('model', 'Focus')
  ]
);

car.model()          // 'Focus'
car.model('Transit') // 'Transit'

      

I know this was asked for a similar question , but this case is slightly different as the method name is also determined at runtime.

+3


source to share


2 answers


If you have dynamic properties that you want to access, use the any type to bypass the type checks on the variable. You can declare a variable of type any from get go, or use the type assertion operator ( like ) at some later point. Here are some possible options:



var car: any = new domain.Entity(...);
car.model();

var car = new domain.Entity(...) as any;
car.model();

var car = new domain.Entity(...);
(car as any).model();

      

0


source


Add this type (just one time):

interface Prop<T> {
    (): T;
    (value: T): T;
}

      



then you can write this for every shape you create:

interface Car extends Entity {
    model: Prop<string>;
    manufacturer: Prop<string>;
}
let car = <Car>new Entity('car', [/*...*/]);
car.model(32); // Error
let x = car.model(); // x: string

      

0


source







All Articles