Home > database >  How to set prototype class fields?
How to set prototype class fields?

Time:01-12

In vanilla JavaScript, it's easy to set up a class' prototype fields:

class Bird {
  static {
    this.prototype.canFly = true;
  }
}
new Bird().canFly // true

However, in TypeScript, the compiler complains that the property doesn't exist. We could mark the line with the error with @ts-ignore and it would work, but then we lose intellisense and type checking for this field.

Also, we can't declare the class field explicitly because then we are expected to initialize the field, which defeats the purpose of setting up the prototype.

class Bird {
  canFly: boolean; // Property 'canFly' has no initializer and is not definitely assigned in the constructor.ts(2564)
  static {
    this.prototype.canFly = true;
  }
}

Even if we @ts-ignore it, the field is still initialized with undefined just for being explicitly declared.

class Bird {
  // @ts-ignore
  canFly: boolean;
  static {
    this.prototype.canFly = true;
  }
}

new Bird().canFly; // undefined

I also tried to "externally" declare the field with declare keyword, but it's also not possible:

class Bird { // Duplicate identifier 'Bird'.ts(2300)
  static {
    this.prototype.canFly = true;
  }
}

declare class Bird { // Duplicate identifier 'Bird'.ts(2300)
  canFly: boolean;
}

Is there a TypeScript way of declaring that the field exists without having to initializing it in the instance?

CodePudding user response:

This seems like a bad idea, but here's an answer anyway.

Just know that I believe that a better answer is "find a different way to that because that looks really bizarre".


Is there a TypeScript way of declaring that the field exists without having to initializing it in the instance?

Yes, with declare.

class Bird {
  declare canFly: boolean

  static set canFly(v: boolean) {
    this.prototype.canFly = true;
  }
}

declare is a type only construct, and tells Typescript to pretend that this exists.

So you declare the canFly property, and now can provide a setter for the prototype value.

console.log(new Bird().canFly) // undefined
Bird.canFly = true
console.log(new Bird().canFly) // true

See Playground


Important note: declare is not type safe, as it just pretends that a value exists there when used. This can cause major type safety issues if misused. Use this almost never in production code, unless you have a really strange situation like this one.


Another approach might be to just keep that value as a static property.

class Bird {
  static canFly: boolean
  canAllBirdsFly() { return Bird.canFly }
}

Which seems much more sane.

CodePudding user response:

Alex Wayne is objectively correct in this. I wish to only add emphasis to part of his answer.

declare is a type only construct, and tells Typescript to pretend that this exists.

declare is a very useful yet very dangerous keyword. It can make your stuff compile just fine, but can actually just break stuff at runtime. I personally only advise its usage when you're trying to adopt an old, tested JS library into your TS code.

declare is like saying: "assume this is true. Even if it isn't, assume it is anyway." Be very cautious with its usage; because there is almost always a better way around it.

  • Related