Roblox-TS - Claude Code Skill
A comprehensive skill for developing Roblox games with TypeScript using roblox-ts.
Overview
Roblox-TS is a TypeScript-to-Luau compiler that allows you to write Roblox games using TypeScript's type safety, modern syntax, and tooling while compiling to performant Luau code.
What This Skill Covers:
-
Setup & Configuration: Project initialization, tsconfig.json, Rojo integration
-
Type-Safe Roblox API: Working with Roblox services, instances, and datatypes
-
TypeScript Features: Using modern TypeScript with Roblox limitations
-
Package Management: Installing and using @rbxts packages from npm
-
Build & Deployment: Compilation, optimization, and publishing workflow
-
Best Practices: Type safety, performance, project structure, and debugging
Documentation Sources:
-
Official roblox-ts docs (https://roblox-ts.com/)
-
TypeScript handbook (adapted for Roblox)
-
Roblox Creator documentation
Quick Start
Installation
Install roblox-ts globally
npm install -g roblox-ts
Create new project
npx rbxtsc init
Or initialize in existing directory
cd my-game npx rbxtsc init
Install packages
npm install
Basic Script Structure
Server Script (src/server/main.server.ts):
import { Players, Workspace } from "@rbxts/services";
Players.PlayerAdded.Connect((player) => {
print(${player.Name} joined the game!);
});
Client Script (src/client/main.client.ts):
import { Players, UserInputService } from "@rbxts/services";
const player = Players.LocalPlayer;
UserInputService.InputBegan.Connect((input) => { if (input.KeyCode === Enum.KeyCode.Space) { print("Space pressed!"); } });
Compilation
Build once
npx rbxtsc
Watch mode (rebuild on file change)
npx rbxtsc -w
Build with verbose output
npx rbxtsc -w --verbose
Core Concepts
Type-Safe Roblox API
All Roblox services and APIs are fully typed:
import { Workspace, Players, ReplicatedStorage } from "@rbxts/services";
// Type-safe instance access const part = Workspace.FindFirstChild("MyPart") as Part | undefined; if (part) { part.BrickColor = BrickColor.Red(); part.Anchored = true; }
// Service usage with intellisense Players.PlayerAdded.Connect((player) => { const character = player.Character || player.CharacterAdded.Wait()[0]; const humanoid = character.FindFirstChildOfClass("Humanoid"); });
TypeScript to Luau Mapping
Arrays:
const array = [1, 2, 3, 4, 5]; array.push(6); const filtered = array.filter(x => x > 3);
Maps (instead of objects for dictionaries):
// Use Map for key-value pairs const playerData = new Map<Player, number>(); playerData.set(player, 100); const health = playerData.get(player);
Sets:
const uniqueIds = new Set<string>(); uniqueIds.add("id1"); uniqueIds.add("id2");
Promises (for async operations):
import { HttpService } from "@rbxts/services";
async function fetchData(url: string) { const response = await HttpService.GetAsync(url); return response; }
Roact (React for Roblox)
import Roact from "@rbxts/roact";
interface Props { text: string; onClick: () => void; }
function Button({ text, onClick }: Props) { return ( <textbutton Size={new UDim2(0, 200, 0, 50)} Text={text} Event={{ MouseButton1Click: onClick }} /> ); }
When to Use This Skill
-
Setting up new roblox-ts projects
-
Configuring tsconfig.json or Rojo projects
-
Converting Lua code to TypeScript
-
Working with type-safe Roblox APIs
-
Installing and using @rbxts packages
-
Understanding TypeScript/Luau differences
-
Debugging compilation or type errors
-
Structuring roblox-ts projects
-
Using modern TypeScript features with Roblox
Detailed Documentation
This skill uses progressive disclosure for efficient context usage:
-
SKILL.md (this file): Quick reference and common patterns (always loaded)
-
Reference files: Detailed documentation (loaded only when needed)
Available Reference Files:
-
SETUP.md - Installation, project structure, configuration
-
TYPESCRIPT.md - TypeScript features, limitations, and idioms
-
ROBLOX_API.md - Type-safe Roblox API usage and patterns
-
PACKAGES.md - Popular @rbxts packages and package management
-
BUILD.md - Compilation, optimization, and deployment
-
MIGRATION.md - Converting Lua to TypeScript
-
BEST_PRACTICES.md - Project structure, performance, debugging
Usage Tips
This skill activates when you mention roblox-ts concepts:
-
"Set up a new roblox-ts project"
-
"Convert this Lua code to TypeScript"
-
"Use RemoteEvents with type safety"
-
"Install Roact for my roblox-ts game"
-
"Fix roblox-ts compilation error"
-
"Structure my TypeScript Roblox project"
For best results:
-
Specify if you're working on server or client scripts
-
Mention specific Roblox services or APIs you're using
-
Share error messages for compilation issues
-
Ask about TypeScript equivalents for Lua patterns
-
Request complete examples for new concepts
Common Patterns
Remote Events with Type Safety
// shared/remotes.ts import { createRemotes, remote } from "@rbxts/remo";
const remotes = createRemotes({ playerDamaged: remote<Server, [targetId: string, damage: number]>(), updateHealth: remote<Client, [health: number]>(), });
export = remotes;
// server/combat.server.ts import remotes from "shared/remotes";
remotes.Server.playerDamaged.connect((player, targetId, damage) => {
// Type-safe parameters
print(${player.Name} damaged ${targetId} for ${damage});
});
// Fire to client remotes.Server.updateHealth.fire(player, 75);
// client/ui.client.ts import remotes from "shared/remotes";
remotes.Client.updateHealth.connect((health) => {
print(Health updated: ${health});
});
// Fire to server remotes.Client.playerDamaged.fire("enemy-123", 25);
Data Stores with Type Safety
import { DataStoreService } from "@rbxts/services";
interface PlayerData { coins: number; level: number; inventory: string[]; }
const dataStore = DataStoreService.GetDataStore("PlayerData");
function savePlayerData(userId: string, data: PlayerData) { const success = pcall(() => { dataStore.SetAsync(userId, data); })[0];
return success;
}
function loadPlayerData(userId: string): PlayerData | undefined { const [success, data] = pcall(() => { return dataStore.GetAsync(userId) as PlayerData | undefined; });
return success ? data : undefined;
}
Character Management
import { Players } from "@rbxts/services";
function setupCharacter(player: Player) { const character = player.Character || player.CharacterAdded.Wait()[0]; const humanoid = character.WaitForChild("Humanoid") as Humanoid; const rootPart = character.WaitForChild("HumanoidRootPart") as Part;
humanoid.Died.Connect(() => {
print(`${player.Name} died`);
});
return { character, humanoid, rootPart };
}
Players.PlayerAdded.Connect((player) => { player.CharacterAdded.Connect(() => { setupCharacter(player); }); });
GUI with Roact
import Roact from "@rbxts/roact"; import { Players } from "@rbxts/services";
interface State { health: number; }
class HealthBar extends Roact.Component<{}, State> { state: State = { health: 100 };
render() {
return (
<screengui>
<frame
Size={new UDim2(0, 200, 0, 30)}
Position={new UDim2(0.5, -100, 0, 10)}
BackgroundColor3={Color3.fromRGB(50, 50, 50)}
>
<frame
Size={new UDim2(this.state.health / 100, 0, 1, 0)}
BackgroundColor3={Color3.fromRGB(0, 255, 0)}
/>
</frame>
</screengui>
);
}
}
const playerGui = Players.LocalPlayer.WaitForChild("PlayerGui"); Roact.mount(<HealthBar />, playerGui);
Configuration Quick Reference
tsconfig.json essentials:
{ "compilerOptions": { "outDir": "out", "rootDir": "src", "module": "commonjs", "strict": true, "noLib": true, "downlevelIteration": true, "moduleResolution": "Node", "resolveJsonModule": true, "types": ["@rbxts/types"] } }
default.project.json (Rojo):
{ "name": "my-game", "tree": { "$className": "DataModel", "ReplicatedStorage": { "$className": "ReplicatedStorage", "rbxts_include": { "$path": "include", "node_modules": { "$path": "node_modules/@rbxts" } } }, "ServerScriptService": { "$className": "ServerScriptService", "TS": { "$path": "out/server" } } } }
Best Practices
-
Use strict type checking - Enable strict: true in tsconfig.json
-
Prefer Maps over objects - For dictionaries with dynamic keys
-
Use proper exports - export = for modules, named exports for utilities
-
Leverage type guards - Use typeIs and classIs for runtime checks
-
Structure by feature - Group related server/client/shared code
-
Use @rbxts packages - Don't reinvent the wheel (Roact, t, remo, etc.)
-
Handle promises properly - Always catch rejections
-
Avoid any type - Use unknown and type guards instead
TypeScript vs Lua Key Differences
Arrays start at 0 (not 1):
const arr = [1, 2, 3]; print(arr[0]); // 1 (TypeScript) // In Lua: arr[1] is 1
Use === for equality:
if (value === undefined) { } // TypeScript // Not: if value == nil then (Lua)
No self keyword:
class MyClass { value = 10;
method() {
print(this.value); // TypeScript uses 'this'
}
}
Destructuring:
const { Name, Health } = player; const [first, second] = array;
Troubleshooting
"Cannot find module '@rbxts/services'" - Run npm install @rbxts/services @rbxts/types
Compilation errors - Check tsconfig.json has correct settings, ensure TypeScript version is compatible
Type errors with Roblox API - Update @rbxts/types package for latest API types
"Expected 'this' in method" - Use arrow functions for callbacks: () => this.method()
Import errors - Use correct export style (export = for modules, export default not supported)
For detailed troubleshooting, see BUILD.md and BEST_PRACTICES.md reference files.
Skill Coverage
Complete roblox-ts Feature Set: ✅ Project setup and configuration
✅ TypeScript compilation to Luau
✅ Full Roblox API type definitions
✅ Package management (@rbxts ecosystem)
✅ Roact/React for UI
✅ Remote communication patterns
✅ Data storage patterns
✅ Build and deployment workflow
✅ Migration from Lua
✅ Performance optimization
Best Practices Emphasized:
-
Strict type safety for reliability
-
Map/Set over objects for collections
-
Proper async handling with Promises
-
Feature-based project structure
-
Leveraging @rbxts package ecosystem
-
Type guards for runtime safety