简体   繁体   中英

Property does not exist on union type on Phaser with TypeScript

I have been following this tutorial, https://learn.yorkcs.com/2019/02/07/build-a-space-shooter-with-phaser-3-3/ , to make games with Phaser 3 but since I want to deploy them online I'm converting the code to use with TypeScript as I am preparing it via an Ionic 5 app. In the tutorial, I'm supposed to set a player sprite's velocity. For clarity, there are two classes I must create, Entities.js (which I created as entities.ts ) and Player.js (which I made into player.ts ). The entities are supposed to be an extension of Phaser.GameObjects.Sprite , and the player is supposed to be an entity, extending the entities class. So far, I get everything working except when reaching the part about setting the player's velocity.

This is my entities.ts :

class Entity extends Phaser.GameObjects.Sprite {
  constructor(scene, x, y, key, type) {
    super(scene, x, y, key);

    this.scene = scene;
    this.scene.add.existing(this);
    this.scene.physics.world.enableBody(this, 0);
    this.setData('type', type);
    this.setData('isDead', false);
  }
}

And this is my player.ts :

class Player extends Entity {
  constructor(scene, x, y, key) {
    super(scene, x, y, key, 'Player');

    this.setData('speed', 200);
    this.play('sprPlayer');
  }

  moveUp() {
    this.body.velocity.y = -this.getData('speed');
  }

  moveDown() {
    this.body.velocity.y = this.getData('speed');
  }

  moveLeft() {
    this.body.velocity.x = -this.getData('speed');
  }

  moveRight() {
    this.body.velocity.x = this.getData('speed');
  }

  update() {
    this.body.setVelocity(0, 0);
  }
}

Once I write the line, this.body.setVelocity(0, 0) , I get an error: Property 'setVelocity' does not exist on type 'Body | StaticBody | BodyType'. Property 'setVelocity' does not exist on type 'StaticBody' Property 'setVelocity' does not exist on type 'Body | StaticBody | BodyType'. Property 'setVelocity' does not exist on type 'StaticBody' Property 'setVelocity' does not exist on type 'Body | StaticBody | BodyType'. Property 'setVelocity' does not exist on type 'StaticBody' . Apparently the body of a sprite can be used for either a Body , StaticBody , or BodyType object. Checking on the documentation, the Body class https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.Body.html allows for the setVelocity function to happen, whereas the StaticBody one https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.StaticBody.html doesn't.

Is it that TypeScript expects for each of the possible types to support the function to be used? If so, is there a way for me to specify what type of body I'm using? I tried parsing to no avail.

Now, I think there may be a way to specify Ionic I wish to develop in plain JavaScript https://uptoskill.com/ionic-react-javascript/ , but without resorting to that, is there a way to specify which type from the union I wish to use in my function calls? Please let me know, and thanks for the help.

The error message indicates that your player body is one of these types 'Body | StaticBody | BodyType' 'Body | StaticBody | BodyType' 'Body | StaticBody | BodyType' but StaticBody does not have a setVelocity method. Typescript has the concept of typeguards to handle this situtation, where you work with union types that have different members.

The solution here, is to check if the this.body has a setVolicity function.

update() {
    // when true typescript know it is not a StaticBody
    if ("setVelocity" in this.body)
    this.body.setVelocity(0, 0);
  }

You could also define a custom typeguard function and use it inside the if statement like this:

//custom typeguard function with the return type 'body is Body'    
function isBody(body: Body | StaticBody): body is Body {
  return (body as Body).setVelocity !== undefined;
}

if (isBody(this.body)) {
  this.body.setVelocity(5); 
}

As explained by jcalz, the answer was to test if the object in question is an instance of the class that contains the function to call. In other words, to ensure that we wish to use Body and not StaticBody . This could have been done by simply checking if the function exists in the object:

if('setVelocity' in this.body) {
  this.body.setVelocity(0, 0);
}

More specifically, we could check if the object is an instance of the object expected, via:

if(this.body instanceof Phaser.Physics.Arcade.Body) {
  this.body.setVelocity(0, 0);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM