Can't access p5.Vector methods in typescript, like random2D() and sub()

Hello, I’m new to p5.js and I’m trying to convert what I learned in a tutorial from the Coding Train into a NextJS app with React and Typescript.

I have this Vehicle class but when I try to use any Vector methods I get that Vector is not found:

import * as p5 from "p5";

export default class Vehicle {
  pos: p5.Vector;
  target: p5.Vector;
  vel: p5.Vector;
  acc: p5.Vector;
  r: number;
  maxspeed: number;
  maxforce: number;

  constructor(p5: p5, x: number, y: number) {
    this.pos = p5.createVector(p5.random(p5.width), p5.random(p5.height));
    this.target = p5.Vector.random2D();
    this.vel = p5.createVector(10,20);
    this.acc = p5.createVector();
    this.r = 15; //radius
    this.maxspeed = 5;
    this.maxforce = 0.1;
  }

   behaviors(p5: p5) {
    let arrive = this.arrive(p5, this.target);
    this.applyForce(arrive);
  }
 
   applyForce(force) {
    this.acc.add(force);
  } 

  seek(p5:p5, target) {
    let desired = p5.Vector.sub(target, this.pos);
    desired.setMag(this.maxspeed);
    let steer = p5.Vector.sub(desired, this.vel);
    steer.limit(this.maxforce);
    return steer;
  } 

  arrive(p5: p5, target: p5.Vector) {
    let desired = p5.Vector.sub(target, this.pos);
    let distance = desired.mag();
    let speed = this.maxspeed;
    if (distance < 100) {
      speed = p5.map(distance, 0, 100, 0, this.maxspeed);
    }
    desired.setMag(speed);
    let steer = p5.Vector.sub(desired, this.vel);
    steer.limit(this.maxforce);
    return steer;
  }


  update() {
    this.vel.add(this.acc);
    this.pos.add(this.vel);
    this.acc.mult(0);
  }

  show(p5: p5) {
    p5.stroke(255);
    p5.strokeWeight(5);
    p5.point(this.pos.x, this.pos.y);
  }
}

according to my package.json I’m using : “@types/p5”: “^1.4.2”, and “react-p5”: “^1.3.30”, as a wrapper. any idea of how to make this work?

The statement import * as p5 from "p5"; makes p5 available as a datatype, but not as the actual p5 constructor.

That is, you can only use p5 to describe things like this after a colon:

  • constructor(p5: p5, x: number, y: number) {
  • behaviors(p5: p5) {
  • pos: p5.Vector;

And if you attempt to access it like this:

  • p5.Vector.random2D();
  • p5.Vector.sub(target, this.pos);
  • new p5(sketch);

It implies that p5 comes straight from the import statement.

However that’s not true, b/c p5 is already available in the global context right after it’s fully loaded within an HTML file:
<script defer src=https://cdn.JsDelivr.net/npm/p5></script>

Unfortunately, for unknown reasons, the package “@types/p5” fails to tell that to the TypeScript compiler.

So from TypeScript’s PoV, library “p5.js” is a module that must be imported rather than a JS regular library which makes itself globally available as property p5.

Luck for you I used to have those same errors and I’ve been forced to code a solution for it already.

It’s a “.d.ts” file which we don’t have to import it, but merely use the triple-slash directive syntax instead:
/// <reference path="../typings/p5-global.d.ts" />

“p5-global.d.ts” describes another p5 constructor that’s exclusively accessed via the window object:

  • window.p5.Vector.random2D();
  • window.p5.Vector.sub(target, this.pos);
  • new window.p5(sketch);

Therefore we can normally continue using p5 as a datatype.

And then switch to window.p5 only when we need to access the real p5 namespace.

I’ve converted & refactored the original “059_Steering_Text_Paths” to TS as an ES6 module instance mode sketch:

You can watch it running online by clicking on this link.

And also download the whole project from here:

2 Likes

Thank you, sir! You saved me. I also took some ideas from your implementation of the Vehicle class, but others didn’t work, I wonder if it’s because I’m in React.

Muito obrigado :slight_smile:

1 Like