Typescript Scripting Guide
TypeScript Scripting Guide
This guide shows how to write PowBot Desktop scripts in TypeScript and compile them to Lua with TypeScriptToLua.
PowBot publishes a TypeScript declaration file named pow_api.d.ts. The desktop client downloads and caches it at %USERPROFILE%/.powbot/pow_api.d.ts next to pow_api.d.lua. That file describes the global PowBot APIs such as logger, game_objects, components, camera, and script_manager.
For the official TypeScriptToLua docs, start here:
When To Use This
Use this workflow when you want:
- Type checking for your script state and options
- Auto-complete against the PowBot API in TypeScript-aware editors
- Refactors and error checking before you run the script
If you want to write plain Lua directly, use the existing Lua guides and pow_api.d.lua docs instead.
Project Layout
A minimal TypeScriptToLua project can look like this:
my-script/
manifest.json
package.json
tsconfig.json
src/
main.ts
Install TypeScriptToLua
Create a package.json and install the compiler locally:
npm install --save-dev typescript typescript-to-lua
This follows the recommended local-install workflow from the TypeScriptToLua docs.
tsconfig.json
PowBot Desktop runs on Lua 5.4, so set luaTarget to 5.4.
{
"$schema": "https://raw.githubusercontent.com/TypeScriptToLua/TypeScriptToLua/master/tsconfig-schema.json",
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext"],
"module": "CommonJS",
"moduleResolution": "Node",
"strict": true,
"rootDir": "src",
"outDir": "dist"
},
"include": ["src/**/*.ts", "%USERPROFILE%/.powbot/pow_api.d.ts"],
"tstl": {
"luaTarget": "5.4",
"buildMode": "default",
"noImplicitGlobalVariables": true
}
}
This setup keeps your generated Lua in dist/ and ensures TypeScript sees both your source files and the local declaration reference.
**Be sure to replace %USERPROFILE%/.powbot/pow_api.d.ts with the full path e.g. C:/Users/Username/.powbot/pow_api.d.ts
package.json
{
"private": true,
"scripts": {
"build": "tstl",
"watch": "tstl --watch"
},
"devDependencies": {
"typescript": "^5.0.0",
"typescript-to-lua": "^1.0.0"
}
}
manifest.json
Your compiled Lua file still needs a normal PowBot manifest.
{
"name": "Quester",
"version": "0.1.0",
"main": "dist/main.lua"
}
Build with:
npm run build
During local development, use this command to recompile as you make changes:
npm run watch
Writing A PowBot Script In TypeScript
The simplest pattern is:
- Define a
Scriptinterface for your lifecycle methods. - Build a plain object that implements it.
- Export the script.
interface Script {
name: string;
version: string;
status_text: string;
options: LuaTable;
state: string;
on_config_request(this: Script, values: LuaTable): LuaTable;
on_button_clicked(this: Script, btn_id: string | number, values: LuaTable): LuaTable;
on_config_changed(this: Script, values: LuaTable, data?: LuaTable): LuaTable;
on_start(this: Script, options_table?: LuaTable): boolean;
poll(this: Script): void;
on_pause(this: Script): void;
on_resume(this: Script): void;
on_stop(this: Script): void;
}
const script: Script = {
name: "Quester",
version: "0.1.0",
status_text: "Idle",
options: {},
state: "IDLE",
on_config_request(this: Script, values: LuaTable): LuaTable {
this.options = values;
logger.info(`Config requested for ${this.name}`);
return values;
},
on_button_clicked(this: Script, btn_id: string | number, values: LuaTable): LuaTable {
this.status_text = `Button clicked: ${btn_id}`;
logger.info(this.status_text);
return values;
},
on_config_changed(this: Script, values: LuaTable, data?: LuaTable): LuaTable {
this.options = values;
this.status_text = "Config updated";
logger.info(this.status_text);
return values;
},
on_start(this: Script, options_table?: LuaTable): boolean {
this.options = options_table ?? this.options;
this.status_text = `Script started: ${this.name} v${this.version}`;
return true;
},
poll(this: Script): void {
this.state = "RUNNING";
this.status_text = `Polling in state ${this.state}`;
const obj = game_objects.find_first({ name: "Gate" });
if (obj && !obj.interact({ action: "Open" })) {
camera.turn_to(obj);
}
logger.info(this.status_text);
sleep(100);
},
on_pause(this: Script): void {
this.state = "PAUSED";
this.status_text = `${this.name} paused`;
logger.info(this.status_text);
},
on_resume(this: Script): void {
this.state = "RUNNING";
this.status_text = `${this.name} resumed`;
logger.info(this.status_text);
},
on_stop(this: Script): void {
this.state = "STOPPED";
this.status_text = `${this.name} stopped`;
logger.info(this.status_text);
}
};
export = script;
Why this: Script Matters
TypeScriptToLua treats function context explicitly. The this: Script parameter tells the compiler that these methods are instance-style methods and keeps this strongly typed inside each lifecycle function.
That is directly aligned with the TypeScriptToLua self and this guidance:
- Use
this: Scriptwhen the function should behave like a Lua method withself - Use
this: voidwhen a callable must not receive a Luaself
For PowBot lifecycle methods such as on_start, poll, and on_stop, you want the script table as context, so this: Script is the correct choice.
Calling PowBot APIs
Use normal TypeScript property access:
logger.info("Hello");
const tree = game_objects.find_first({ name: "Tree" });
const player = players.local_player();
The provided declarations are written so TypeScriptToLua can emit the correct Lua call style for the PowBot runtime.
Compiled Output
In the sample tsconfig.json above, outDir is set to dist, so that configuration writes output to:
dist/
main.lua
Without outDir, TypeScriptToLua writes emitted .lua files next to the corresponding .ts source files.
Example default layout:
src/
main.ts
main.lua
Use the compiled .lua file as your PowBot script entrypoint in manifest.json.
Notes And Limitations
pow_api.d.tsis generated from the same source aspow_api.d.lua, so editor help and runtime docs stay aligned.- If you need to model callbacks that must not receive a Lua context, follow the official
this: voidguidance from the TypeScriptToLua self-parameter docs.
Recommended Workflow
- Create
manifest.jsonand point itsmainfield at your compiled Lua entrypoint, usuallydist/main.lua. - Add a local
types.d.tsthat references%USERPROFILE%/.powbot/pow_api.d.ts. - Write your script in
src/main.ts. - Run
npm run watchwhile developing so your Lua output stays up to date as you edit. - Load the generated Lua file in PowBot Desktop.
- Use
npm run buildwhen you want a one-off build instead of watch mode.