HowItWorks TypeScript - grakkit/grakkit GitHub Wiki

Home | How To | How It Works | Samples | More

Index | Environment | TypeScript

TypeScript In Grakkit

Typescript is a language built on-top of Javascript. It has all the features of Javascript, support for new features not included in ECMAScript yet (particularly many stage 3 proposals), and of course TS-specific features like their type system.

Typescript is whole different territory, so if you haven't delved into it yet we would recommend you first check it out or just skip this section.

Walk-Through Example

Add this event handler to user.js. When any player in the game moves, the log shows a message.

core.event('org.bukkit.event.player.PlayerMoveEvent', (event) => {
      const player = event.getPlayer();
      console.log(`player moved: ${player.getName()}`);
})

Mouseover (event) to see (parameter) event: PlayerMoveEvent.
(Don't confuse the core.event, event property on the core object, with the event parameter in the function.)
How does it know the event parameter expected is of type PlayerMoveEvent?

We know the event type will be 'org.bukkit.event.player.PlayerMoveEvent'. That is, for the event 'org.bukkit.event.player.PlayerMoveEvent', the object passed to the event listener/handler is 'org.bukkit.event.player.PlayerMoveEvent'. For us there is always that one-for-one correspondence. But How does VS Code know that? The answer is more complicated.

When the core object is defined in index.js, it reads and imports the file core.d.ts. As noted above, that file imports types from files types.d.ts and events.d.ts. The interface for the core object is also defined. That interface includes a prototype for the event function:

event: typeof events.event;

Looking at this more closely: event is a property of the core object. In JavaScript a property can be an object/data or it can be a function. As a property can have a type, so can a function. In Object Oriented Programming (OOP), when a function supports multiple argument types, it's called "overloaded". When we use core.event(), there are over 300 types of events that can be handled. Each of these includes a string value as a name, and a 'listener', which is a function that accepts an event parameter.

The line above tells us the event function can be of type event, which is defined in the events type. In events.d.ts we see one of these definitions, an "overload" :

static event (
      name: 'org.bukkit.event.player.PlayerMoveEvent',
      ...listeners: ((event: classes.PlayerMoveEvent) => {})[]
   ): void;

That can be translated as : When the name value for the event function is 'org.bukkit.event.player.PlayerMoveEvent', the value of listeners will be a function which, when the event occurs, will get an object of type classes.PlayerMoveEvent.

Following that back now to answer to the original question: When core.event() has the name parameter set to 'org.bukkit.event.player.PlayerMoveEvent', that prototype overload says the event object will be of type PlayerMoveEvent.

Looking back at the event handler, we see const player = event.getPlayer();.
How does it know the event has a getPlayer() method?

Look in classes.d.ts for the prototype of the PlayerMoveEvent (which is the object type of the event parameter). It starts with this:

export class PlayerMoveEvent extends PlayerEvent implements Cancellable

That PlayerMoveEvent class does not include a definition for getPlayer(). But the class extends ( = inherits = derives from) PlayerEvent.

export class PlayerEvent extends Event {
   constructor (who: Player);
   constructor (who: Player, async: boolean);
   getPlayer (): Player;
}

So PlayerEvent derives from the Event class. It includes everything defined for a generic Event, and it adds a getPlayer() function, which returns an object of type Player. Since PlayerMoveEvent derives from PlayerEvent, PlayerEvent has a getPlayer() function.

The final line of the event handler is:

console.log(`player moved: ${player.getName()}`);

We know the player variable is of type Player. This is also described in classes.p.ts as:

export interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, NetworkClient {

The Player interface itself does not include an explicit definition for a getName() function, but once again, Player extends HumanEntity, and that prototype includes a definition for getName().

Remember that all of this is purely for the developer's convenience in the VS Code environment. The actual functionality is defined by the Bukkit API, implemented by Spigot, enahanced by Paper, and executed on the Minecraft core.

⚠️ **GitHub.com Fallback** ⚠️