TypeScript and type generation
PixelScript writes TypeScript definitions for the Java classes your scripts actually use, and it can run
.ts files directly. Together these give you real autocomplete and real type checking against the Bukkit
API.
definitions.d.ts
The runtime generates scripts/definitions.d.ts on boot, and again whenever your scripts reach for
something new.
What ends up in it:
- Every global the platform binds:
Bukkit,Script,Watcher,Scheduler,Sql,DataFile,registerCommand,registerListener,fetch, and the rest - Every class you have loaded with
Script.loadClass - Every class resolved through
$ - Everything those classes reference in their signatures, so
player.getLocation()returns aLocationthat is actually defined - Classes from jars added with
Script.loadLibraryorScript.loadGradleDependency
The file starts with //@ts-nocheck and declares globals inside declare global, with Java packages
mirrored as TypeScript namespaces:
declare global {
const Script: virtualclass_dev.pixelib.pixelscript.SimpleScript;
const Bukkit: org.bukkit.Server;
const Scheduler: dev.pixelib.pixelscript.script.extensions.SchedulerExtension$ScriptScheduler;
function registerCommand(commandName: string, callbackWithSenderAndArgs: (sender: org.bukkit.command.CommandSender, args: string[]) => void): void;
function registerListener<Ctor>(eventClass: Ctor, listener: (event: InstanceType<Ctor>) => void, eventPriority: EventPriority): void;
}
Two consequences worth knowing:
The file grows as you use more classes. A class only becomes a full definition once a script imports it. This keeps the file to a manageable size instead of emitting every class on the classpath.
Autocomplete for a brand new class appears after the next reload. Load the class, save, let the script reload, and your IDE picks up the regenerated file.
Never edit definitions.d.ts by hand and never commit it. Put it in your .gitignore.
tsconfig.json
The generated tsconfig.json wires everything together:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["*"]
},
"allowJs": true,
"checkJs": false
},
"files": ["definitions.d.ts"],
"include": ["**/*.ts", "**/*.js"]
}
filespulls in the generated globalspathsmaps the@/import alias to the scripts root, so your IDE resolves@/utils/messagesallowJswithcheckJs: falsemeans your.jsfiles get completion without being type checked
Turn on checkJs if you want type errors in plain JavaScript too. Expect noise the first time.
Writing .ts scripts
Any .ts file in your scripts folder is compiled to JavaScript at load time and then behaves exactly like
a .js script. Root scripts, watched scripts and imported modules can all be TypeScript.
// init.ts
const SystemInstance = Script.loadClass('java.lang.System');
const serverType: string = SystemInstance.getenv("DC_SERVER_TYPE") || "park";
Watcher.watch([
'patches/init.js'
]);
switch (serverType) {
case "park":
Watcher.watch('servers/park/init.js');
break;
case "lobby":
Watcher.watch('servers/lobby/init.js');
break;
default:
log('Unknown server type: ' + serverType);
Bukkit.shutdown();
break;
}
You can mix freely. A .ts file can import a .js module and the other way around, and the extension is
optional in import paths: the resolver tries .js first and falls back to .ts.
Compilation details
Files are compiled with target: ESNEXT and module: ESNEXT, with missing imports and names tolerated
(the generated globals are declared, not imported). Compilation is lenient by design: type errors are
reported to the console with a [TSC] prefix but do not stop your script from loading.
That is a deliberate trade-off. Hot reloading stays fast and a type complaint never takes a feature
offline. It does mean the console is where TypeScript errors go, not the reload. If you want type
errors to block, run tsc --noEmit in your editor or CI.
Source maps are not generated, so a runtime stack trace points at the compiled JavaScript. Keep that in
mind when debugging a .ts file.
.d.ts files are never loaded as scripts
Files ending in .d.ts are explicitly excluded from script discovery, so definitions.d.ts sitting in
your scripts root is not treated as a root script.
Practical setup
- Open
plugins/PixelScript(orplugins/PixelScript/scripts) as your IDE project root sotsconfig.jsonis picked up. - Add
definitions.d.tsto.gitignore. - Work against a local server, so regenerated definitions land in your working copy immediately. See Setting up your work environment.
- Use
@/imports so your IDE and the runtime agree on where a module lives.
Status
TypeScript support is newer than the rest of the runtime. The compilation path is stable, but the tooling around it (stricter checking, source maps, editor integration) is still being built out. Plain JavaScript remains the well-trodden path if you want zero surprises.