# Table of Contents - [Introduction | roblox-ts](#introduction-roblox-ts) - [Constructors | roblox-ts](#constructors-roblox-ts) - [Functions | roblox-ts](#functions-roblox-ts) - [Roblox API | roblox-ts](#roblox-api-roblox-ts) - [PowerShell errors with "running scripts is disabled on this system" | roblox-ts](#powershell-errors-with-running-scripts-is-disabled-on-this-system-roblox-ts) - [npm publish errors with "npm error 402 Payment Required" | roblox-ts](#npm-publish-errors-with-npm-error-402-payment-required-roblox-ts) - [Frequently Asked Questions | roblox-ts](#frequently-asked-questions-roblox-ts) - [Utility Types | roblox-ts](#utility-types-roblox-ts) - [npm publish errors with "npm error 404 Not Found" | roblox-ts](#npm-publish-errors-with-npm-error-404-not-found-roblox-ts) - [Quick Start | roblox-ts](#quick-start-roblox-ts) - [Usage | roblox-ts](#usage-roblox-ts) - [Setup Guide | roblox-ts](#setup-guide-roblox-ts) - [Luau Packages | roblox-ts](#luau-packages-roblox-ts) - [Indexing Children | roblox-ts](#indexing-children-roblox-ts) - [Callbacks vs Methods | roblox-ts](#callbacks-vs-methods-roblox-ts) - [DataType Math | roblox-ts](#datatype-math-roblox-ts) - [LuaTuple | roblox-ts](#luatuple-t-roblox-ts) - [TypeScript Transformers | roblox-ts](#typescript-transformers-roblox-ts) - [Using Existing Luau | roblox-ts](#using-existing-luau-roblox-ts) - [TypeScript Packages | roblox-ts](#typescript-packages-roblox-ts) - [Syncing with Rojo | roblox-ts](#syncing-with-rojo-roblox-ts) - [Roact JSX | roblox-ts](#roact-jsx-roblox-ts) --- # Introduction | roblox-ts [Skip to main content](https://roblox-ts.com/docs/#__docusaurus_skipToContent_fallback) On this page What is roblox-ts?[​](https://roblox-ts.com/docs/#what-is-roblox-ts "Direct link to What is roblox-ts?") --------------------------------------------------------------------------------------------------------- In short, roblox-ts is a way to use the tooling and ecosystem surrounding TypeScript for Roblox development. This includes intellisense, IDE extensions, linters, code formatters, and more! It allows you to write TypeScript code that is then compiled into Luau code for use inside of Roblox. roblox-ts works with both **TypeScript** files (`.ts`) _and_ **Luau** files (`.lua`) which are paired with TypeScript type definition files (`.d.ts`). Typings for the Roblox API are partially handwritten and partially automatically generated from the Roblox API dump and Developer Hub documentation. Features[​](https://roblox-ts.com/docs/#features "Direct link to Features") ---------------------------------------------------------------------------- * Write TypeScript code, run it in Roblox * Automatic project structure setup for creating games, models, plugins, and packages * Automatically generated type definitions for the Roblox API * Infers the structure of your game using your Rojo configuration file * Use VSCode's intellisense, autocompletion, and type checking abilities * Use industry standard tools for linting and formatting like [ESLint](https://eslint.org/) and [Prettier](https://prettier.io/) * [Growing collection of NPM packages](https://www.npmjs.com/org/rbxts) * [TypeScript Transformer Plugins](https://github.com/madou/typescript-transformer-handbook) Why should you use roblox-ts?[​](https://roblox-ts.com/docs/#why-should-you-use-roblox-ts "Direct link to Why should you use roblox-ts?") ------------------------------------------------------------------------------------------------------------------------------------------ roblox-ts is great for managing large scale projects. Static types and intellisense allow you and your IDE to understand what your code is doing on a deeper level without having to mentally track what's going on. TypeScript's typing system allows you to clearly describe what each function expects as inputs and gives back as an output. roblox-ts, TypeScript, and most of the related tooling is open source! This means you can hack it, tweak it, and customize it to your liking. roblox-ts supports [TypeScript transformer plugins](https://roblox-ts.com/docs/guides/typescript-transformers) which allow you to modify the behavior of the compiler itself! You can also write your own ESLint plugins to enforce certain rules on your code. Why should you _not_ use roblox-ts?[​](https://roblox-ts.com/docs/#why-should-you-not-use-roblox-ts "Direct link to why-should-you-not-use-roblox-ts") ------------------------------------------------------------------------------------------------------------------------------------------------------- The biggest downside to using roblox-ts is debugging. When you use the Roblox Studio debugger (or when you encounter an error), you will see the compiled Luau code instead of TypeScript code. While we actively work to make the compiled Luau code as readable as possible, this does still introduce a bit of complexity. Usually, it's pretty easy to mentally map your compiled Luau code to your TypeScript code. The silver lining here is that the remaining few bugs should be logical bugs anyway! Learning Resources[​](https://roblox-ts.com/docs/#learning-resources "Direct link to Learning Resources") ---------------------------------------------------------------------------------------------------------- Need help? Feel free to ask us questions in our [Discord server's #help channel](https://discord.roblox-ts.com/) ! * [TypeScript for the New Programmer](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html) * [TypeScript Tooling in 5 minutes](https://www.typescriptlang.org/docs/handbook/typescript-tooling-in-5-minutes.html) * [Learn X in Y minutes TypeScript Walk-through](https://learnxinyminutes.com/docs/typescript/) * [What is roblox-ts?](https://roblox-ts.com/docs/#what-is-roblox-ts) * [Features](https://roblox-ts.com/docs/#features) * [Why should you use roblox-ts?](https://roblox-ts.com/docs/#why-should-you-use-roblox-ts) * [Why should you _not_ use roblox-ts?](https://roblox-ts.com/docs/#why-should-you-not-use-roblox-ts) * [Learning Resources](https://roblox-ts.com/docs/#learning-resources) --- # Constructors | roblox-ts [Skip to main content](https://roblox-ts.com/docs/api/constructors#__docusaurus_skipToContent_fallback) On this page ### `new Array(length?: number, value?: T)`[​](https://roblox-ts.com/docs/api/constructors#new-arraytlength-number-value-t "Direct link to new-arraytlength-number-value-t") Creates a new `Array` type with the given preallocated length and pre-filled with `value`. Compiles to either a table literal or a `table.create()` call in Luau. ### `new Map(entries?: Array<[K, V]>)`[​](https://roblox-ts.com/docs/api/constructors#new-mapk-ventries-arrayk-v "Direct link to new-mapk-ventries-arrayk-v") Creates a new `Map` type pre-filled with the entries given as the argument if provided. Compiles to a table literal. ### `new ReadonlyMap(entries?: Array<[K, V]>)`[​](https://roblox-ts.com/docs/api/constructors#new-readonlymapk-ventries-arrayk-v "Direct link to new-readonlymapk-ventries-arrayk-v") Same as `new Map`, but creates a `ReadonlyMap` type instead. ### `new WeakMap(entries?: Array<[K, V]>)`[​](https://roblox-ts.com/docs/api/constructors#new-weakmapk-ventries-arrayk-v "Direct link to new-weakmapk-ventries-arrayk-v") Same as `new Map`, but creates a `WeakMap` type instead. Compiles using `setmetatable({}, { __mode = "k" })`. ### `new Set(values?: Array)`[​](https://roblox-ts.com/docs/api/constructors#new-setvvalues-arrayt "Direct link to new-setvvalues-arrayt") Creates a new `Set` type pre-filled with the values given as the argument if provided. Compiles to a table literal. ### `new ReadonlySet(values?: Array)`[​](https://roblox-ts.com/docs/api/constructors#new-readonlysetvvalues-arrayt "Direct link to new-readonlysetvvalues-arrayt") Same as `new Set`, but creates a `ReadonlySet` type instead. ### `new WeakSet(values?: Array)`[​](https://roblox-ts.com/docs/api/constructors#new-weaksetvvalues-arrayt "Direct link to new-weaksetvvalues-arrayt") Same as `new Set`, but creates a `WeakSet` type instead. Compiles using `setmetatable({}, { __mode = "k" })`. ### `new Promise()`[​](https://roblox-ts.com/docs/api/constructors#new-promiset "Direct link to new-promiset") Creates a new `Promise` type. You can find documentation for the promise library that comes bundled with roblox-ts [here](https://eryn.io/roblox-lua-promise/) . * [`new Array(length?: number, value?: T)`](https://roblox-ts.com/docs/api/constructors#new-arraytlength-number-value-t) * [`new Map(entries?: Array<[K, V]>)`](https://roblox-ts.com/docs/api/constructors#new-mapk-ventries-arrayk-v) * [`new ReadonlyMap(entries?: Array<[K, V]>)`](https://roblox-ts.com/docs/api/constructors#new-readonlymapk-ventries-arrayk-v) * [`new WeakMap(entries?: Array<[K, V]>)`](https://roblox-ts.com/docs/api/constructors#new-weakmapk-ventries-arrayk-v) * [`new Set(values?: Array)`](https://roblox-ts.com/docs/api/constructors#new-setvvalues-arrayt) * [`new ReadonlySet(values?: Array)`](https://roblox-ts.com/docs/api/constructors#new-readonlysetvvalues-arrayt) * [`new WeakSet(values?: Array)`](https://roblox-ts.com/docs/api/constructors#new-weaksetvvalues-arrayt) * [`new Promise()`](https://roblox-ts.com/docs/api/constructors#new-promiset) --- # Functions | roblox-ts [Skip to main content](https://roblox-ts.com/docs/api/functions#__docusaurus_skipToContent_fallback) On this page ### `assert()`[​](https://roblox-ts.com/docs/api/functions#assert "Direct link to assert") roblox-ts's assert uses "JavaScript truthiness" for it's condition. This means that `""` (empty string), `0`, and `NaN` values will cause the assertion to fail in addition to `undefined` and `false`. The reason for this is so that we can take advantage of TypeScript's `asserts value` predicate feature. function foo(instance: Instance) { assert(instance.IsA("Part")); print(instance.Size); // instance _must_ be a Part to reach this line} Because of this change, `assert(0)` or `assert("")` will cause an error in roblox-ts, but not in Luau. ### `typeOf()`[​](https://roblox-ts.com/docs/api/functions#typeof "Direct link to typeof") Unfortunately, TypeScript already has an operator named "typeof" in the form of `typeof x`. Because of this, we cannot expose the Luau `typeof()` function directly. To get around this, we compile `typeOf(value)` into `typeof(value)`. ### `typeIs()`[​](https://roblox-ts.com/docs/api/functions#typeis "Direct link to typeis") Checking types with `typeOf` is usually not very useful with roblox-ts unless you need the string value that is returned. This is because TypeScript cannot infer that your if-statement confirmed the value was type checked: function foo(value: unknown) { if (typeOf(value) === "Vector3") { print(value.X); // error: value is still unknown! }} To get around this, `typeIs(value, "type")` compiles to `typeof(value) == "type"` and helps TypeScript infer the value was type checked: function foo(value: unknown) { if (typeIs(value, "Vector3")) { print(value.X); // success! }} ### `classIs()`[​](https://roblox-ts.com/docs/api/functions#classis "Direct link to classis") Similar to `typeIs`, `classIs(value, "ClassName")` compiles to `value.ClassName == "ClassName"`. This is useful for cases where you might want to avoid `instance.IsA()`. function foo(value: Instance) { // value.IsA("Script") would return true for LocalScripts! if (classIs(value, "Script")) { print(value.Name); }} ### `identity()`[​](https://roblox-ts.com/docs/api/functions#identity "Direct link to identity") The `identity` macro compiles to just the inner value you pass into it, allowing for a zero-cost type constraint abstraction. This is useful for verifying that a given value is the type you expect: interface MyInterface { a: number; b: string; c: boolean;}const objects = { abc: identity({ a: 123, b: "abc", c: true, }),}; * [`assert()`](https://roblox-ts.com/docs/api/functions#assert) * [`typeOf()`](https://roblox-ts.com/docs/api/functions#typeof) * [`typeIs()`](https://roblox-ts.com/docs/api/functions#typeis) * [`classIs()`](https://roblox-ts.com/docs/api/functions#classis) * [`identity()`](https://roblox-ts.com/docs/api/functions#identity) --- # Roblox API | roblox-ts [Skip to main content](https://roblox-ts.com/docs/api/roblox-api#__docusaurus_skipToContent_fallback) On this page roblox-ts provides typings for the Roblox API which are partially handwritten and partially automatically generated. In general, everything from the Roblox API is available for use, unless it is deprecated or conflicts with TypeScript. Values[​](https://roblox-ts.com/docs/api/roblox-api#values "Direct link to Values") ------------------------------------------------------------------------------------ ### Globals[​](https://roblox-ts.com/docs/api/roblox-api#globals "Direct link to Globals") All global values from the Roblox API are present in roblox-ts typings. print("Hello World!"); const zero = math.sin(math.pi); coroutine.wrap(() => { print("A"); wait(1); print("B");})(); You can find a list of available globals and libraries on the [Roblox Engine API Reference](https://create.roblox.com/docs/reference/engine) . Parts of `table` and `string` have been intentionally omitted. ### Constructors[​](https://roblox-ts.com/docs/api/roblox-api#constructors "Direct link to Constructors") `.new()` functions (like `Vector3.new()` or `CFrame.new()`) should instead be called with the `new` operator. `new T(...)` will always compile to `T.new(...)`. const v3 = new Vector3(1, 2, 3); // compiles to Vector3.new(1, 2, 3)print(v3.X, v3.Y, v3.Z); // 1 2 3 const part = new Instance("Part");print(part.Color); ### nil[​](https://roblox-ts.com/docs/api/roblox-api#nil "Direct link to nil") `undefined` is a direct replacement for `nil`. It can be used both as a type and a value. Types[​](https://roblox-ts.com/docs/api/roblox-api#types "Direct link to Types") --------------------------------------------------------------------------------- ### Provided Types[​](https://roblox-ts.com/docs/api/roblox-api#provided-types "Direct link to Provided Types") Every Roblox class (`Instance`, `Part`, `Humanoid`, `Workspace`, etc.) is provided as a global/ambient type. You can use these types to describe variables, function parameters, function return types, and just about anything else in your code. // note: The type Part could be inferred here if not providedconst part: Part = new Instance("Part");print(part.Size); function takesBasePart(basePart: BasePart) { return basePart.Size.X + basePart.Size.Y + basePart.Size.Z;}// we can use any type which inherits from BasePart!takesBasePart(new Instance("Seat"));takesBasePart(new Instance("Part"));takesBasePart(new Instance("WedgePart"));// Humanoid does not inherit from BasePart, so this will error!// takesBasePart(new Instance("Humanoid")); ### RemoteEvent Types[​](https://roblox-ts.com/docs/api/roblox-api#remoteevent-types "Direct link to RemoteEvent Types") New roblox-ts users are usually confused why `RemoteEvent.OnServerEvent` only allows `unknown` arguments. const remoteEvent = new Instance("RemoteEvent");// this works fineremoteEvent.OnClientEvent.Connect((points: number) => {});// changing unknown to number causes an error!remoteEvent.OnServerEvent.Connect((player: Player, points: unknown) => {}); The reason for this is because client-to-server networking _cannot_ be trusted. Exploiters or cheaters in your game can fire your RemoteEvent functions with whatever data they want. If your code is expecting `points` to be a `number`, but the client sends a `nil` value, the server will error. By sending a large number of requests very quickly which result in errors, an exploiter could crash your game server and disconnect all of your players. Instead, you should assume your inputs can be any possible value and validate the type at runtime using the `typeIs` macro: const remoteEvent = new Instance("RemoteEvent");remoteEvent.OnServerEvent.Connect((player: Player, points: unknown) => { if (!typeIs(points, "number")) { return; } // do something with points}); You can also verify the arguments with a type validation package like [`@rbxts/t`](https://www.npmjs.com/package/@rbxts/t) . Alternatively, the community has created a few networking libraries which make the experience much nicer! * [@rbxts/net](https://www.npmjs.com/package/@rbxts/net) * [@rbxts/networked-signals](https://www.npmjs.com/package/@rbxts/networked-signals) * [@rbxts/remo](https://www.npmjs.com/package/@rbxts/remo) ### Exceptions[​](https://roblox-ts.com/docs/api/roblox-api#exceptions "Direct link to Exceptions") Deprecated types are usually not provided. Exceptions to this rule are made for API members which do not have a non-deprecated functional equivalent. One notable exception: `Instance.Changed` is not provided as it conflicts with inheritance. Usually, you want to use `Instance.GetPropertyChangedSignal()` instead. import { Workspace } from "@rbxts/services";Workspace.GetPropertyChangedSignal("DistributedGameTime").Connect(() => { print(Workspace.DistributedGameTime);}); If you _must_ use `Instance.Changed`, you can workaround this by asserting an intersection type with `ChangedSignal`: function foo(part: Part) { (part as Part & ChangedSignal).Changed.Connect(name => {})} Utility Interfaces[​](https://roblox-ts.com/docs/api/roblox-api#utility-interfaces "Direct link to Utility Interfaces") ------------------------------------------------------------------------------------------------------------------------ There are a few key global interfaces which make manipulating types easier: ### Services[​](https://roblox-ts.com/docs/api/roblox-api#services "Direct link to Services") `Services` is an interface consisting of a mapping of string name to type for every Roblox service which you can fetch with `game:GetService("ServiceName")`. interface Services { AnalyticsService: AnalyticsService; AppUpdateService: AppUpdateService; AssetCounterService: AssetCounterService; AssetDeliveryProxy: AssetDeliveryProxy; // ... many more services!} You can get a union of all service names with `keyof Services`. And you can get a union of all service types with `Services[keyof Services]`. type AllServiceNames = keyof Services;type AllServices = Services[keyof Services]; ### CreatableInstances[​](https://roblox-ts.com/docs/api/roblox-api#creatableinstances "Direct link to CreatableInstances") `CreatableInstances` is an interface consisting of a mapping of string name to type for every Roblox instance which can be created with `Instance.new("ClassName")`. interface CreatableInstances { Accessory: Accessory; Accoutrement: Accoutrement; Actor: Actor; AlignOrientation: AlignOrientation; // ... many more instances!} You can get a union of all creatable instance names with `keyof CreatableInstances`. And you can get a union of all creatable instance types with `CreatableInstances[keyof CreatableInstances]`. type AllCreatableInstanceNames = keyof CreatableInstances;type AllCreatableInstances = CreatableInstances[keyof CreatableInstances]; ### AbstractInstances[​](https://roblox-ts.com/docs/api/roblox-api#abstractinstances "Direct link to AbstractInstances") `AbstractInstances` is an interface consisting of a mapping of string name to type for every Roblox instance which will _never_ be created. Generally, these are useful for functions that check inheritance like `Instance:IsA("ClassName")`. interface AbstractInstances { BackpackItem: BackpackItem; BasePart: BasePart; BasePlayerGui: BasePlayerGui; BaseScript: BaseScript; // ... many more instances!} You can get a union of all abstract instance names with `keyof AbstractInstances`. And you can get a union of all abstract instance types with `AbstractInstances[keyof AbstractInstances]`. type AllAbstractInstanceNames = keyof AbstractInstances;type AllAbstractInstances = AbstractInstances[keyof AbstractInstances]; ### Instances[​](https://roblox-ts.com/docs/api/roblox-api#instances "Direct link to Instances") `Instances` is an interface consisting of a mapping of string name to type for every Roblox instance. It inherits from `Services`, `CreatableInstances`, and `AbstractInstances`. `Instances` and also includes any type of Instance which: * cannot be created with `Instance.new("ClassName")` * cannot be fetched with `game:GetService("ServiceName")` * but _can_ be given a reference to Examples: * `DataModel` is a class that cannot be created or fetched with GetService, but is given by the `game` global value * `AnimationTrack` can only be given a reference to via `:LoadAnimation()` from `Humanoid` or `AnimationTrack` interface Instances extends Services, CreatableInstances, AbstractInstances { AnimationTrack: AnimationTrack; BaseWrap: BaseWrap; CatalogPages: CatalogPages; DataModel: DataModel; // ... many more instances!} You can get a union of all instance names with `keyof Instances`. And you can get a union of all instance types with `Instances[keyof Instances]`. type AllInstanceNames = keyof Instances;type AllInstances = Instances[keyof Instances]; ### Usage with Generics[​](https://roblox-ts.com/docs/api/roblox-api#usage-with-generics "Direct link to Usage with Generics") You can use any of these utility interfaces with generic functions. This is useful for taking an argument string and returning a matching instance type. import { Workspace } from "@rbxts/services";function getDescendantsWhichIsA(parent: Instance, className: T): Instances[T][] { return parent.GetDescendants().filter((descendant): descendant is Instances[T] => descendant.IsA(className));}const humanoidsInWorkspace: Array = getDescendantsWhichIsA(Workspace, "Humanoid"); * [Values](https://roblox-ts.com/docs/api/roblox-api#values) * [Globals](https://roblox-ts.com/docs/api/roblox-api#globals) * [Constructors](https://roblox-ts.com/docs/api/roblox-api#constructors) * [nil](https://roblox-ts.com/docs/api/roblox-api#nil) * [Types](https://roblox-ts.com/docs/api/roblox-api#types) * [Provided Types](https://roblox-ts.com/docs/api/roblox-api#provided-types) * [RemoteEvent Types](https://roblox-ts.com/docs/api/roblox-api#remoteevent-types) * [Exceptions](https://roblox-ts.com/docs/api/roblox-api#exceptions) * [Utility Interfaces](https://roblox-ts.com/docs/api/roblox-api#utility-interfaces) * [Services](https://roblox-ts.com/docs/api/roblox-api#services) * [CreatableInstances](https://roblox-ts.com/docs/api/roblox-api#creatableinstances) * [AbstractInstances](https://roblox-ts.com/docs/api/roblox-api#abstractinstances) * [Instances](https://roblox-ts.com/docs/api/roblox-api#instances) * [Usage with Generics](https://roblox-ts.com/docs/api/roblox-api#usage-with-generics) --- # PowerShell errors with "running scripts is disabled on this system" | roblox-ts [Skip to main content](https://roblox-ts.com/docs/faq/powershell#__docusaurus_skipToContent_fallback) Attempting to run `rbxtsc` in PowerShell results in an error like this: ![image](https://i.imgur.com/0e2RGHE.png) This happens if PowerShell is not configured to run scripts downloaded from the internet (like roblox-ts). You can allow this by running the following command in PowerShell as an administrator: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser` You can run PowerShell as an administrator by right-clicking the Windows logo in the bottom left corner and selecting "Windows PowerShell (Admin)" ![image](https://i.imgur.com/G9T20og.png) [More information here](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.1) --- # npm publish errors with "npm error 402 Payment Required" | roblox-ts [Skip to main content](https://roblox-ts.com/docs/faq/publish-as-public#__docusaurus_skipToContent_fallback) On this page When publishing a package for the first time, you might encounter an error like this: npm error code E402npm error 402 Payment Required - PUT https://registry.npmjs.org/@rbxts%2fpackage - You must sign up for private packages This happens because if you're publishing to a new package to an organization, it's private by default. However, private packages require a paid npm org (which we don't have). There are two ways to fix this issue: ### 1\. `package.json` `publishConfig` setting[​](https://roblox-ts.com/docs/faq/publish-as-public#1-packagejson-publishconfig-setting "Direct link to 1-packagejson-publishconfig-setting") Add the following to your `package.json`. { "name": "@rbxts/package", // your package name goes here "version": "1.0.0", "publishConfig": { "access": "public" } // other fields} note If you're using the [`npm init roblox-ts package` command](https://roblox-ts.com/docs/guides/typescript-packages) , this will be done for you automatically. ### 2\. `npm publish` CLI flag[​](https://roblox-ts.com/docs/faq/publish-as-public#2-npm-publish-cli-flag "Direct link to 2-npm-publish-cli-flag") npm publish --access public * [1\. `package.json` `publishConfig` setting](https://roblox-ts.com/docs/faq/publish-as-public#1-packagejson-publishconfig-setting) * [2\. `npm publish` CLI flag](https://roblox-ts.com/docs/faq/publish-as-public#2-npm-publish-cli-flag) --- # Frequently Asked Questions | roblox-ts [Skip to main content](https://roblox-ts.com/docs/faq#__docusaurus_skipToContent_fallback) * [PowerShell errors with "running scripts is disabled on this system"](https://roblox-ts.com/docs/faq/powershell) * [npm publish errors with "npm error 404 Not Found"](https://roblox-ts.com/docs/faq/publish-not-found) * [npm publish errors with "npm error 402 Payment Required"](https://roblox-ts.com/docs/faq/publish-as-public) --- # Utility Types | roblox-ts [Skip to main content](https://roblox-ts.com/docs/api/utility-types#__docusaurus_skipToContent_fallback) On this page ### `_`[​](https://roblox-ts.com/docs/api/utility-types#_t "Direct link to _t") Placeholder that sometimes helps force TS to display what you want it to. type _ = T; ### `Partial`[​](https://roblox-ts.com/docs/api/utility-types#partialt "Direct link to partialt") Make all properties in T optional type Partial = { [P in keyof T]?: T[P] }; ### `Required`[​](https://roblox-ts.com/docs/api/utility-types#requiredt "Direct link to requiredt") Make all properties in T required type Required = { [P in keyof T]-?: T[P] }; ### `Readonly`[​](https://roblox-ts.com/docs/api/utility-types#readonlyt "Direct link to readonlyt") Make all properties in T readonly type Readonly = { readonly [P in keyof T]: T[P] }; ### `Writable`[​](https://roblox-ts.com/docs/api/utility-types#writablet "Direct link to writablet") Make all properties in T non-readonly. type Writable = { -readonly [P in keyof T]: T[P] }; ### `Pick`[​](https://roblox-ts.com/docs/api/utility-types#pickt-k "Direct link to pickt-k") From T pick a set of properties K type Pick = { [P in K]: T[P] }; ### `Omit`[​](https://roblox-ts.com/docs/api/utility-types#omitt-k "Direct link to omitt-k") Returns a subset of type T which excludes properties K type Omit = Pick>; ### `Record`[​](https://roblox-ts.com/docs/api/utility-types#recordk-t "Direct link to recordk-t") Construct a type with a set of properties K of type T type Record = { [P in K]: T }; ### `Exclude`[​](https://roblox-ts.com/docs/api/utility-types#excludet-u "Direct link to excludet-u") Exclude from T those types that are assignable to U type Exclude = T extends U ? never : T; ### `Extract`[​](https://roblox-ts.com/docs/api/utility-types#extractt-u "Direct link to extractt-u") Extract from T those types that are assignable to U type Extract = T extends U ? T : never; ### `ExtractKeys`[​](https://roblox-ts.com/docs/api/utility-types#extractkeyst-u "Direct link to extractkeyst-u") Returns a union of all the keys of T whose values extend from U type ExtractKeys = { [K in keyof T]: T[K] extends U ? K : never }[keyof T]; ### `ExtractMembers`[​](https://roblox-ts.com/docs/api/utility-types#extractmemberst-u "Direct link to extractmemberst-u") Returns a new object type of all the keys of T whose values extend from U type ExtractMembers = Pick>; ### `ExcludeKeys`[​](https://roblox-ts.com/docs/api/utility-types#excludekeyst-u "Direct link to excludekeyst-u") Returns a union of all the keys of T whose values do not extend from U type ExcludeKeys = { [K in keyof T]: T[K] extends U ? never : K }[keyof T]; ### `ExcludeMembers`[​](https://roblox-ts.com/docs/api/utility-types#excludememberst-u "Direct link to excludememberst-u") Returns a new object type of all the keys of T whose values do not extend from U type ExcludeMembers = Pick>; ### `NonNullable`[​](https://roblox-ts.com/docs/api/utility-types#nonnullablet "Direct link to nonnullablet") Exclude null and undefined from T type NonNullable = unknown extends T ? defined : T extends null | undefined ? never : T; ### `Parameters`[​](https://roblox-ts.com/docs/api/utility-types#parameterst "Direct link to parameterst") Obtain the parameters of a function type in a `tuple | never`. type Parameters any> = T extends (...args: infer P) => any ? P : never; ### `ConstructorParameters`[​](https://roblox-ts.com/docs/api/utility-types#constructorparameterst "Direct link to constructorparameterst") Obtain the parameters of a constructor function type in a `tuple | never` type ConstructorParameters any> = T extends new (...args: infer P) => any ? P : never; ### `ReturnType`[​](https://roblox-ts.com/docs/api/utility-types#returntypet "Direct link to returntypet") Obtain the return type of a function type type ReturnType) => any> = T extends (...args: Array) => infer R ? R : any; ### `InstanceType`[​](https://roblox-ts.com/docs/api/utility-types#instancetypet "Direct link to instancetypet") Obtain the return type of a constructor function type type InstanceType) => any> = T extends new (...args: Array) => infer R ? R : any; ### `Reconstruct`[​](https://roblox-ts.com/docs/api/utility-types#reconstructt "Direct link to reconstructt") Combines a series of intersections into one object, e.g. { x: number } & { y: number } becomes { x: number, y: number } type Reconstruct = _<{ [K in keyof T]: T[K] }>; ### `UnionToIntersection`[​](https://roblox-ts.com/docs/api/utility-types#uniontointersectiont "Direct link to uniontointersectiont") Converts a series of object unions to a series of intersections, e.g. A | B becomes A & B type UnionToIntersection = (T extends object ? (k: T) => void : never) extends (k: infer U) => void ? U : never; ### `ThisParameterType`[​](https://roblox-ts.com/docs/api/utility-types#thisparametertypet "Direct link to thisparametertypet") Extracts the type of the 'this' parameter of a function type, or 'unknown' if the function type has no 'this' parameter. type ThisParameterType = T extends (this: infer U, ...args: Array) => any ? U : unknown; ### `OmitThisParameter`[​](https://roblox-ts.com/docs/api/utility-types#omitthisparametert "Direct link to omitthisparametert") Removes the 'this' parameter from a function type. type OmitThisParameter = unknown extends ThisParameterType ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T; ### `WritablePropertyNames`[​](https://roblox-ts.com/docs/api/utility-types#writablepropertynamest "Direct link to writablepropertynamest") Given an object `T`, returns a unioned type of all non-readonly property names. type WritablePropertyNames = { [K in keyof T]-?: T[K] extends Callback ? never : (() => F extends { [Q in K]: T[K] } ? 1 : 2) extends () => F extends { -readonly [Q in K]: T[K]; } ? 1 : 2 ? K : never;}[keyof T]; ### `WritableProperties`[​](https://roblox-ts.com/docs/api/utility-types#writablepropertiest "Direct link to writablepropertiest") Given an object `T`, returns an object with readonly fields filtered out. type WritableProperties = Pick>; ### `InstancePropertyNames`[​](https://roblox-ts.com/docs/api/utility-types#instancepropertynamest "Direct link to instancepropertynamest") Given an Instance `T`, returns a unioned type of all property names. type InstancePropertyNames = ExcludeKeys; ### `InstanceMethodNames`[​](https://roblox-ts.com/docs/api/utility-types#instancemethodnamest "Direct link to instancemethodnamest") Given an Instance `T`, returns a unioned type of all method names. type InstanceMethodNames = ExtractKeys; ### `InstanceEventNames`[​](https://roblox-ts.com/docs/api/utility-types#instanceeventnamest "Direct link to instanceeventnamest") Given an Instance `T`, returns a unioned type of all event names. type InstanceEventNames = ExtractKeys; ### `InstanceProperties`[​](https://roblox-ts.com/docs/api/utility-types#instancepropertiest "Direct link to instancepropertiest") Given an Instance `T`, returns an object with only properties. type InstanceProperties = Pick>; ### `InstanceMethods`[​](https://roblox-ts.com/docs/api/utility-types#instancemethodst "Direct link to instancemethodst") Given an Instance `T`, returns an object with only methods. type InstanceMethods = Pick>; ### `InstanceEvents`[​](https://roblox-ts.com/docs/api/utility-types#instanceeventst "Direct link to instanceeventst") Given an Instance `T`, returns an object with only events. type InstanceEvents = Pick>; ### `WritableInstanceProperties`[​](https://roblox-ts.com/docs/api/utility-types#writableinstancepropertiest "Direct link to writableinstancepropertiest") Given an Instance `T`, returns an object with readonly fields, methods, and events filtered out. type WritableInstanceProperties = WritableProperties>; ### `ExcludeNominalMembers`[​](https://roblox-ts.com/docs/api/utility-types#excludenominalmemberst "Direct link to excludenominalmemberst") Returns a new object type of all the keys of T which do not start with `_nominal_` type ExcludeNominalMembers = Pick>; * [`_`](https://roblox-ts.com/docs/api/utility-types#_t) * [`Partial`](https://roblox-ts.com/docs/api/utility-types#partialt) * [`Required`](https://roblox-ts.com/docs/api/utility-types#requiredt) * [`Readonly`](https://roblox-ts.com/docs/api/utility-types#readonlyt) * [`Writable`](https://roblox-ts.com/docs/api/utility-types#writablet) * [`Pick`](https://roblox-ts.com/docs/api/utility-types#pickt-k) * [`Omit`](https://roblox-ts.com/docs/api/utility-types#omitt-k) * [`Record`](https://roblox-ts.com/docs/api/utility-types#recordk-t) * [`Exclude`](https://roblox-ts.com/docs/api/utility-types#excludet-u) * [`Extract`](https://roblox-ts.com/docs/api/utility-types#extractt-u) * [`ExtractKeys`](https://roblox-ts.com/docs/api/utility-types#extractkeyst-u) * [`ExtractMembers`](https://roblox-ts.com/docs/api/utility-types#extractmemberst-u) * [`ExcludeKeys`](https://roblox-ts.com/docs/api/utility-types#excludekeyst-u) * [`ExcludeMembers`](https://roblox-ts.com/docs/api/utility-types#excludememberst-u) * [`NonNullable`](https://roblox-ts.com/docs/api/utility-types#nonnullablet) * [`Parameters`](https://roblox-ts.com/docs/api/utility-types#parameterst) * [`ConstructorParameters`](https://roblox-ts.com/docs/api/utility-types#constructorparameterst) * [`ReturnType`](https://roblox-ts.com/docs/api/utility-types#returntypet) * [`InstanceType`](https://roblox-ts.com/docs/api/utility-types#instancetypet) * [`Reconstruct`](https://roblox-ts.com/docs/api/utility-types#reconstructt) * [`UnionToIntersection`](https://roblox-ts.com/docs/api/utility-types#uniontointersectiont) * [`ThisParameterType`](https://roblox-ts.com/docs/api/utility-types#thisparametertypet) * [`OmitThisParameter`](https://roblox-ts.com/docs/api/utility-types#omitthisparametert) * [`WritablePropertyNames`](https://roblox-ts.com/docs/api/utility-types#writablepropertynamest) * [`WritableProperties`](https://roblox-ts.com/docs/api/utility-types#writablepropertiest) * [`InstancePropertyNames`](https://roblox-ts.com/docs/api/utility-types#instancepropertynamest) * [`InstanceMethodNames`](https://roblox-ts.com/docs/api/utility-types#instancemethodnamest) * [`InstanceEventNames`](https://roblox-ts.com/docs/api/utility-types#instanceeventnamest) * [`InstanceProperties`](https://roblox-ts.com/docs/api/utility-types#instancepropertiest) * [`InstanceMethods`](https://roblox-ts.com/docs/api/utility-types#instancemethodst) * [`InstanceEvents`](https://roblox-ts.com/docs/api/utility-types#instanceeventst) * [`WritableInstanceProperties`](https://roblox-ts.com/docs/api/utility-types#writableinstancepropertiest) * [`ExcludeNominalMembers`](https://roblox-ts.com/docs/api/utility-types#excludenominalmemberst) --- # npm publish errors with "npm error 404 Not Found" | roblox-ts [Skip to main content](https://roblox-ts.com/docs/faq/publish-not-found#__docusaurus_skipToContent_fallback) When publishing a package for the first time, you might encounter an error like this: npm error code E404npm error 404 Not Found - PUT https://registry.npmjs.org/@rbxts%2package - Not foundnpm error 404npm error 404 '@rbxts/package@1.0.0' is not in this registry.npm error 404npm error 404 Note that you can also install from anpm error 404 tarball, folder, http url, or git url. This is due to a bug with our npm join-org page. We are working on a fix for this issue. In the mean time, please reach out to Osyris on Discord with your npm username. --- # Quick Start | roblox-ts [Skip to main content](https://roblox-ts.com/docs/quick-start#__docusaurus_skipToContent_fallback) Need help? Feel free to ask us questions in our [Discord server's #help channel](https://discord.roblox-ts.com/) ! roblox-ts makes it _really_ easy to get started! 1. Run `npm init roblox-ts` command to start the interactive project setup tool 2. Run `npx rbxtsc -w` to start watch mode 3. In another terminal, run `rojo serve` to start Rojo 4. Open a Roblox Studio place and connect your Rojo plugin 5. Write code! 🎉 [If you need more help, you can find a detailed guide here.](https://roblox-ts.com/docs/setup-guide) --- # Usage | roblox-ts [Skip to main content](https://roblox-ts.com/docs/usage#__docusaurus_skipToContent_fallback) The following is a reference for how to use the roblox-ts command line interface. This can be displayed with `rbxtsc --help`. roblox-ts - A TypeScript-to-Luau Compiler for RobloxCommands: rbxtsc build Build a project [default]Options: -p, --project project path [string] [default: "."] -w, --watch enable watch mode [boolean] [default: false] --usePolling use polling for watch mode [boolean] [default: false] --verbose enable verbose logs [boolean] [default: false] --noInclude do not copy include files [boolean] [default: false] --logTruthyChanges logs changes to truthiness evaluation from Lua truthiness rules [boolean] [default: false] --writeOnlyChanged [boolean] [default: false] --writeTransformedFiles writes resulting TypeScript ASTs after transformers to out directory [boolean] [default: false] --optimizedLoops [boolean] [default: false] --type override project type [choices: "game", "model", "package"] -i, --includePath folder to copy runtime files to [string] --rojo manually select Rojo project file [string] --allowCommentDirectives [boolean] [default: false] -h, --help show help information [boolean] -v, --version show version information [boolean] --- # Setup Guide | roblox-ts [Skip to main content](https://roblox-ts.com/docs/setup-guide#__docusaurus_skipToContent_fallback) On this page Prerequisites * Familiarity with the command line. You can find tutorials on YouTube for [Windows](https://www.youtube.com/watch?v=FiTZgpRpWv0) or [MacOS](https://www.youtube.com/watch?v=aKRYQsKR46I) * [NodeJS 18+](https://nodejs.org/) * A code editor such as [VSCode](https://code.visualstudio.com/) * [Rojo 7+](https://rojo.space/) which can be installed through the [Rojo VSCode plugin](https://marketplace.visualstudio.com/items?itemName=evaera.vscode-rojo) . PowerShell If you're using PowerShell for the first time, you may get an error about "running scripts is disabled on this system". You can fix this by running the following in PowerShell as an administrator: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser` [More info here](https://roblox-ts.com/docs/faq/powershell) Project Setup[​](https://roblox-ts.com/docs/setup-guide#project-setup "Direct link to Project Setup") ------------------------------------------------------------------------------------------------------ In this guide, we will introduce how to create a roblox-ts game. Make sure you have an up-to-date version of Node.js installed and your current working directory is an empty folder that you intend to create your project in. Run the following command in your command line: Terminal npm init roblox-ts The tool will guide you through some questions to set up your project: 1. `Project directory` - Since you're already in the directory you want to create a project in, just enter a period (`.`) to select your current directory and press `Enter`. 2. `Select template` - What type of roblox-ts project is this? roblox-ts can be used to build plugins and packages too, but in this case we want to make a game, so press `Enter` once. 3. `Configure Git` - Do you want to use [Git](https://git-scm.com/) for version control? Press `Y` or `N` to make your choice. 4. `Configure ESLint` - Do you want to use [ESLint](https://eslint.org/) to check your code for correctness? 5. `Configure Prettier` - Do you want to use [Prettier](https://prettier.io/) to format your code? 6. `Configure VSCode Project Settings` - Do you want the tool to create a `.vscode` folder with settings optimized for roblox-ts development? If you're using VSCode to write code, you should use this. 7. `Multiple package managers detected. Select package manager:` - Which package manager do you want to use with this project? roblox-ts works best with `npm` and support for alternative package managers is not guaranteed. (If you don't have other package managers installed, this step won't appear!) tip Many of the tools used here can be directly integrated into VSCode with extensions. We highly recommend the roblox-ts extension to improve your editing experience. The roblox-ts extension not only provides extra diagnostic information, but also lets you build your code with one click. * [roblox-ts VSCode extension](https://marketplace.visualstudio.com/items?itemName=Roblox-TS.vscode-roblox-ts) * [ESLint VSCode extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) * [Prettier VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) * [Rojo VSCode extension](https://marketplace.visualstudio.com/items?itemName=evaera.vscode-rojo) Then, open your project in your code editor of choice, such as VSCode: Terminal code project-name Running Your Code[​](https://roblox-ts.com/docs/setup-guide#running-your-code "Direct link to Running Your Code") ------------------------------------------------------------------------------------------------------------------ roblox-ts has a feature called "watch mode" where it will continuously build your code as you write it. You can start watch mode from the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=Roblox-TS.vscode-roblox-ts) or via the command line: Terminal npm run watch Once roblox-ts is running, you need to start Rojo to start syncing your code into Roblox studio you can do this from the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=evaera.vscode-rojo) or via the command line: Terminal rojo serve You're all set up, go write some code 🎉 * [Project Setup](https://roblox-ts.com/docs/setup-guide#project-setup) * [Running Your Code](https://roblox-ts.com/docs/setup-guide#running-your-code) --- # Luau Packages | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/luau-packages#__docusaurus_skipToContent_fallback) TODO --- # Indexing Children | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/indexing-children#__docusaurus_skipToContent_fallback) On this page New roblox-ts users often ask, "How can I index a child in the game like `game.Workspace.Zombie`?" The problem here is that you are making an assumption that `Zombie` is something that exists inside `Workspace`. In Roblox, if you try to index the child of an instance that does not exist, it results in an error. Even if roblox-ts allowed you to do this, `Zombie` would be of type `Instance` which is not particularly helpful! Creating `services.d.ts`[​](https://roblox-ts.com/docs/guides/indexing-children#creating-servicesdts "Direct link to creating-servicesdts") -------------------------------------------------------------------------------------------------------------------------------------------- The simplest solution to this problem is to extend the types of your services to describe the state of your DataModel. We can do this through "ambient type declarations". An ambient type definition file is a `.d.ts` file which contains no imports or exports. This file becomes global and the types are available in every file without importing. We can name this file anything and put it anywhere in `src`, but for this example we'll stick to `src/services.d.ts`. src/services.d.ts interface Workspace extends Instance { Zombie: Model;} // some other fileimport { Workspace } from "@rbxts/services";print(Workspace.Zombie); Heads up! This example needs the `@rbxts/services` package, which is not included by default. If you haven't already, run `npm install @rbxts/services` to install it. [playground-friendly example](https://roblox-ts.com/playground/#code/KYDwDg9gTgLgBAbwL4G4BQATYBjANgQymDgHNcIAjfXRNOeuASwDsZgoAzfbYgdWgDWAZzDdioNswxC4ASWZCY+Zj1oN1cAFoQAthUbAAXHACyELLnTqkaG2mwQF8AF679xALyl8O4ADp+KGFRHj9tPQN0MCgWGAAKVwjgAEp0IA) If you want to define children inside of Zombie, you can do this with an intersection object type: src/services.d.ts interface Workspace extends Instance { Zombie: Model & { Humanoid: Humanoid; };} // some other fileimport { Workspace } from "@rbxts/services";print(Workspace.Zombie.Humanoid); [playground-friendly example](https://roblox-ts.com/playground/#code/KYDwDg9gTgLgBAbwL4G4BQATYBjANgQymDgHNcIAjfXRNASAEsA7GYKAM322IHVoBrAM5guxUKyYZBcAJJNBMfE2606dAFoQAthQbAAXHACyELDQBkqtQAkArlqUQGGQ3YdMnGdGtT0kafzRsCHl4AAt7R2c4AF5SfC1gADo+KCERbiTNHT0ktyivNDAoZhgACgj3TwBKdCA) note If you're having compiler errors where the property is still not defined on an object, even after creating a types file, you might need to restart the compiler! "rbxts-object-to-tree" plugin by Validark[​](https://roblox-ts.com/docs/guides/indexing-children#rbxts-object-to-tree-plugin-by-validark "Direct link to "rbxts-object-to-tree" plugin by Validark") ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ### Usage[​](https://roblox-ts.com/docs/guides/indexing-children#usage "Direct link to Usage") Writing out long `services.d.ts` files is tedious! And a mismatch between your type definition files and actual game could be dangerous. Luckily, there's an existing plugin by Validark called "rbxts-object-to-tree" that you can use to automatically generate these typings. [https://www.roblox.com/library/3379119778/rbxts-object-to-tree](https://www.roblox.com/library/3379119778/rbxts-object-to-tree) 1. Simply select the service you want to generate typings for in the Explorer pane. ![](data:image/png;base64,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) 2. Click "TS types" and "GENERATE" ![](https://roblox-ts.com/assets/images/rbxts-object-to-tree-6c32cbce085834155a16c736fe6cef7c.png) 3. Your type definition file will be created in Lighting and you can copy that over into `src/services.d.ts` ![](data:image/png;base64,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) types/ReplicatedStorage.d.ts interface ReplicatedStorage extends Instance { Zombie: Model & { ["Left Leg"]: Part; Humanoid: Humanoid; ["Right Leg"]: Part; Head: Part & { Mesh: SpecialMesh; Face: Decal; }; Torso: Part; HumanoidRootPart: Part; ["Right Arm"]: Part; ["Left Arm"]: Part; ["Body Colors"]: BodyColors; };} ### io-serve[​](https://roblox-ts.com/docs/guides/indexing-children#io-serve "Direct link to io-serve") Alternatively, to make things even more automatic you can use [io-serve](https://www.npmjs.com/package/io-serve) by Evaera to automatically write the .d.ts files to your filesystem. First, make sure HTTP requests are enabled for your game in Game Settings or by running `game.HttpService.HttpEnabled = true` in the command bar. Then, simply just run `npx io-serve` in your project folder before hitting "GENERATE" in rbxts-object-to-tree. `npx` will automatically download and then run io-serve for you. This will generate a file located at `types/ReplicatedStorage.d.ts`. * [Creating `services.d.ts`](https://roblox-ts.com/docs/guides/indexing-children#creating-servicesdts) * ["rbxts-object-to-tree" plugin by Validark](https://roblox-ts.com/docs/guides/indexing-children#rbxts-object-to-tree-plugin-by-validark) * [Usage](https://roblox-ts.com/docs/guides/indexing-children#usage) * [io-serve](https://roblox-ts.com/docs/guides/indexing-children#io-serve) --- # Callbacks vs Methods | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/callbacks-vs-methods#__docusaurus_skipToContent_fallback) On this page To begin, let's quickly define two Luau terms: 1. A "callback" is a function that is called in the form of `foo.bar()`. 2. A "method" is a function that is called in the form of `foo:bar()`. * The parameter `self` is implicitly passed as the value of `foo`. However, in TypeScript, all functions inside of objects are called as simply `foo.bar()`. To decide whether or not a function call should compile using `.` or `:`, roblox-ts follows a simple set of rules: Callbacks[​](https://roblox-ts.com/docs/guides/callbacks-vs-methods#callbacks "Direct link to Callbacks") ---------------------------------------------------------------------------------------------------------- * Function declarations are considered **callbacks**. function foo(bar: number) {}const obj = { foo: foo };obj.foo(123); // obj.foo(123) * Arrow function expressions are considered **callbacks**. const obj = { foo: (bar: number) => {}}obj.foo(123); // obj.foo(123) Methods[​](https://roblox-ts.com/docs/guides/callbacks-vs-methods#methods "Direct link to Methods") ---------------------------------------------------------------------------------------------------- * Method declarations are considered **methods**. const obj = { foo(bar: number) {}}obj.foo(123); // obj:foo(123) * Function expressions inside of object literals are considered **methods**. const obj = { foo: function (bar: number) {}}obj.foo(123); // obj:foo(123) Overrides[​](https://roblox-ts.com/docs/guides/callbacks-vs-methods#overrides "Direct link to Overrides") ---------------------------------------------------------------------------------------------------------- * If a function has a parameter `this: void`, it is _always_ considered to be a **callback**. const obj = { foo(this: void, bar: number) {}}obj.foo(123); // obj.foo(123) * If a function has a parameter `this` which is typed as anything except `void`, it is _always_ considered to be a **method**. declare const obj: { foo: (this: typeof obj, bar: number) => void;}obj.foo(123); // obj:foo(123) * [Callbacks](https://roblox-ts.com/docs/guides/callbacks-vs-methods#callbacks) * [Methods](https://roblox-ts.com/docs/guides/callbacks-vs-methods#methods) * [Overrides](https://roblox-ts.com/docs/guides/callbacks-vs-methods#overrides) --- # DataType Math | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/datatype-math#__docusaurus_skipToContent_fallback) [Roblox features a bunch of "DataType" classes](https://developer.roblox.com/en-us/api-reference/data-types) and many of these use operator overloading for math operations. i.e. `Vector2`, `Vector3`, `CFrame` Unfortunately, TypeScript does not have a way to describe operator overloading with types. To get around this, roblox-ts adds four macro methods `.add()`, `.sub()`, `.mul()`, and `.div()` to DataType classes which support math operators. * `a.add(b)` compiles to `a + b` * `a.sub(b)` compiles to `a - b` * `a.mul(b)` compiles to `a * b` * `a.div(b)` compiles to `a / b` [You can see an up to date list of classes which support math operators here.](https://github.com/roblox-ts/types/blob/master/include/macro_math.d.ts) --- # LuaTuple | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/lua-tuple#__docusaurus_skipToContent_fallback) On this page The Problem[​](https://roblox-ts.com/docs/guides/lua-tuple#the-problem "Direct link to The Problem") ----------------------------------------------------------------------------------------------------- Given the following Luau code, how do we type it? local function foo() return "abc", 123end Multiple returns are a common occurrence in Luau, but not really a concept in TypeScript. [TypeScript does have a feature called "tuples"](https://www.typescriptlang.org/docs/handbook/basic-types.html#tuple) which allow for fixed length array types with an individual type for each index. So we might type this as: declare function foo(): [string, number]; However, this is not correct! TypeScript tuples are arrays, not multiple returns. It would expect the Luau to be `return { "abc", 123 }`. The Solution[​](https://roblox-ts.com/docs/guides/lua-tuple#the-solution "Direct link to The Solution") -------------------------------------------------------------------------------------------------------- To solve this problem, roblox-ts introduces a special type called `LuaTuple`. This is typed as: type LuaTuple> = T & { readonly LUA_TUPLE: never }; `LUA_TUPLE` is used to ensure the type name is not lost or cast into an `Array` accidentally. When the compiler sees this type as a function return type, it can infer that it's meant to be a multiple return. In general, this type is used for typing existing Luau modules or the Roblox API. Examples[​](https://roblox-ts.com/docs/guides/lua-tuple#examples "Direct link to Examples") -------------------------------------------------------------------------------------------- If you immediately destructure the result, it will be compiled into a simple variable declaration from a multiple return. const [actualTimeYielded, totalTime] = wait(1); If you do _not_ destructure the result, the compiler will wrap the return in `{ }` and turn the result into an array object. const result = wait(1);const actualTimeYielded = result[0];const totalTime = result[1]; You can also index the result immediately after the function call and receive an optimized emit: import { Players } from "@rbxts/services";// .Wait() here returns LuaTuple<[character: Model]>,// so we need to use `[0]` to grab the first (and only) element.const character = Players.LocalPlayer.Character || Players.LocalPlayer.CharacterAdded.Wait()[0]; Using `LuaTuple` in Your Own Code[​](https://roblox-ts.com/docs/guides/lua-tuple#using-luatuplet-in-your-own-code "Direct link to using-luatuplet-in-your-own-code") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ The compiler supports using `LuaTuple` in outside of type definitions, but it's generally not recommended. However, if you're writing code that is consumed by an existing Luau module this can be useful! Fortunately, we have a macro called `$tuple` that covers multiple returns so that you don't need to type assert the function return yourself every time you want it to be a tuple. function hasMultipleReturns() { // this will compile into `return "abc", 123` return $tuple("abc", 123);} * [The Problem](https://roblox-ts.com/docs/guides/lua-tuple#the-problem) * [The Solution](https://roblox-ts.com/docs/guides/lua-tuple#the-solution) * [Examples](https://roblox-ts.com/docs/guides/lua-tuple#examples) * [Using `LuaTuple` in Your Own Code](https://roblox-ts.com/docs/guides/lua-tuple#using-luatuplet-in-your-own-code) --- # TypeScript Transformers | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/typescript-transformers#__docusaurus_skipToContent_fallback) On this page Introduction[​](https://roblox-ts.com/docs/guides/typescript-transformers#introduction "Direct link to Introduction") ---------------------------------------------------------------------------------------------------------------------- TypeScript transformer plugins are powerful extensions to the compiler which rewrite your TypeScript AST before it reaches roblox-ts. Some transformer plugins which are made for vanilla TypeScript also work for roblox-ts. Your milage may vary here. [You can find a list of transformer plugins for vanilla TypeScript here.](https://github.com/cevek/ttypescript#transformers) roblox-ts Transformer Plugins[​](https://roblox-ts.com/docs/guides/typescript-transformers#roblox-ts-transformer-plugins "Direct link to roblox-ts Transformer Plugins") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The community has written some transformer plugins specifically for roblox-ts. You can find a list of these below: * [rbxts-transform-debug](https://www.npmjs.com/package/rbxts-transform-debug) * [rbxts-transform-env](https://www.npmjs.com/package/rbxts-transform-env) * [rbxts-transformer-services](https://www.npmjs.com/package/rbxts-transformer-services) * [rbxts-transformer-t](https://www.npmjs.com/package/rbxts-transformer-t) * [Introduction](https://roblox-ts.com/docs/guides/typescript-transformers#introduction) * [roblox-ts Transformer Plugins](https://roblox-ts.com/docs/guides/typescript-transformers#roblox-ts-transformer-plugins) --- # Using Existing Luau | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/using-existing-luau#__docusaurus_skipToContent_fallback) On this page roblox-ts lets you use existing Luau scripts in your TypeScript codebase. All you have to do is define the types in a type declaration file (`.d.ts`). ### Creating Type Declaration Files[​](https://roblox-ts.com/docs/guides/using-existing-luau#creating-type-declaration-files "Direct link to Creating Type Declaration Files") roblox-ts will copy any non-compiled file (anything that isn't a `.ts` or `.tsx` file) into your `outDir` (out). This lets you place `.lua` files in your `rootDir` (src) and they will be copied into the `outDir` (out) and then synced into Roblox Studio using Rojo. To create a type declaration file (`.d.ts`) for this `.lua` file, simply create a file next to it using the same name. ![](data:image/png;base64,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) As an exception, if the name of your `.lua` file is `init.lua`, your type definition file needs to be named `index.d.ts`. ### Modeling Module Return Values[​](https://roblox-ts.com/docs/guides/using-existing-luau#modeling-module-return-values "Direct link to Modeling Module Return Values") Luau scripts return a single value at the end in the form of: local Module = {}-- define Module membersreturn Module To model this return value in TypeScript types, we can use an "Export Assignment" statement: interface Module { // define Module member types}// create a value from our typedeclare const Module: Module;export = Module; And you could import it as: import Module from "./Module";print(Module); If a module uses a table return containing static fields/functions, it may be preferrable to use normal exports. local MyConstants = {}MyConstants.Foo = "Bar"MyConstants.Secret = "hunter2"return MyConstants Then your types could look like this: export declare const Foo: string;export declare const Secret: string; And you could import them as: import { Foo, Secret } from "./MyConstants";print(Foo, Secret); ### Typings for Custom Classes[​](https://roblox-ts.com/docs/guides/using-existing-luau#typings-for-custom-classes "Direct link to Typings for Custom Classes") A common way to describe custom OOP classes is with a `MyClass` interface, `MyClassConstructor` interface, and a `MyClass` variable of type `MyClassConstructor`. interface MyClass { instanceProperty: string; instanceMethod(): number;}interface MyClassConstructor { new (): MyClass; staticProperty: string; staticMethod(): number;}declare const MyClass: MyClassConstructor;export = MyClass; Then in another file, you can use this as: print(MyClass.staticProperty);print(MyClass.staticMethod());const myClass = new MyClass();print(myClass.instanceProperty);print(myClass.instanceMethod()); ### Related Articles[​](https://roblox-ts.com/docs/guides/using-existing-luau#related-articles "Direct link to Related Articles") * [`LuaTuple`](https://roblox-ts.com/docs/guides/lua-tuple) * [Callbacks vs Methods](https://roblox-ts.com/docs/guides/callbacks-vs-methods) * [Creating Type Declaration Files](https://roblox-ts.com/docs/guides/using-existing-luau#creating-type-declaration-files) * [Modeling Module Return Values](https://roblox-ts.com/docs/guides/using-existing-luau#modeling-module-return-values) * [Typings for Custom Classes](https://roblox-ts.com/docs/guides/using-existing-luau#typings-for-custom-classes) * [Related Articles](https://roblox-ts.com/docs/guides/using-existing-luau#related-articles) --- # TypeScript Packages | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/typescript-packages#__docusaurus_skipToContent_fallback) On this page note To publish roblox-ts packages, you'll need to join the "@rbxts" npm organization. [You can do that here.](https://roblox-ts.com/join-org) Getting Started[​](https://roblox-ts.com/docs/guides/typescript-packages#getting-started "Direct link to Getting Started") --------------------------------------------------------------------------------------------------------------------------- Creating packages with roblox-ts is super easy! To start, just create an empty folder and run `npm init roblox-ts package` inside of it. This will generate a project scaffolding for your package. You can author your `.ts` files inside of the `src` folder. In `package.json`, you can edit a few different fields to configure your package: * "name" - This _must_ begin with `@rbxts/` to be considered a valid roblox-ts package. * "description" * "main" - This should point to a `.lua` file in `out` which represents your package's entrypoint. * "typings" - This should point to a `.d.ts` file in `out` which represents your package's entrypoint. * "files" - An array of globs for what should be published to npm. Defaults to `["out"]`. You don't explicitly have to specify special files like `package.json`, `LICENSE` or `readme.md` here, as npm includes those by default. * "repository" - If your package's source is public on something like GitHub, you should include a link to it in this field. * "homepage" - If your package has online documentation, you should include a link to it in this field. * "author" - Your own name or username. * "license" - The license of your package's code. This should be the [SPDX license identifier](https://spdx.org/licenses/) that corresponds to your `LICENSE` file. Testing Your Package[​](https://roblox-ts.com/docs/guides/typescript-packages#testing-your-package "Direct link to Testing Your Package") ------------------------------------------------------------------------------------------------------------------------------------------ The simplest way to test your package is with `npm pack`. This command will generate a `.tgz` file which can then be used from another project via `npm install ../../path/to/package.tgz`. warning When publishing a package for the first time, you might run into this issue: [npm publish errors with "npm error 402 Payment Required"](https://roblox-ts.com/docs/faq/publish-as-public) * [Getting Started](https://roblox-ts.com/docs/guides/typescript-packages#getting-started) * [Testing Your Package](https://roblox-ts.com/docs/guides/typescript-packages#testing-your-package) --- # Syncing with Rojo | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/syncing-with-rojo#__docusaurus_skipToContent_fallback) On this page Introduction[​](https://roblox-ts.com/docs/guides/syncing-with-rojo#introduction "Direct link to Introduction") ---------------------------------------------------------------------------------------------------------------- The role of roblox-ts is to turn TypeScript (`.ts`) files into Luau files (`.lua`). That alone isn't very useful! You need to move the files into Roblox Studio to be able to use them for a game, plugin, model, etc. To do this, you can use [Rojo](https://rojo.space/) ! Rojo takes files from your local filesystem and will either: * turn them into a single Roblox file (`.rbxm`, `.rbxl`, `.rbxmx`, or `.rbxlx`) via `rojo build` * sync them into an open Roblox Studio session via `rojo serve` Rojo uses a `default.project.json` file to describe how files should be organized within a Roblox file. Heads up! Your `default.project.json` file should have all `"$path"` fields relative to your `outDir` (which is the `"out"` folder by default). The workflow should look like this: `.ts` files in `src` ⬇️ roblox-ts via `rbxtsc` ⬇️ `.lua` files in `out` ⬇️ Rojo via `rojo build` or `rojo serve` ⬇️ Roblox Studio 🎉 roblox-ts will use your `project.json` file to understand how TypeScript (`.ts`) files eventually end up inside of Roblox Studio. This is primarily used for compiling import statements. To use a different `project.json` file instead of `default.project.json` for compiling, you can use the `--rojo` flag: rbxtsc --rojo other.project.json Customization[​](https://roblox-ts.com/docs/guides/syncing-with-rojo#customization "Direct link to Customization") ------------------------------------------------------------------------------------------------------------------- Like any other Rojo project, you can organize a roblox-ts project however you'd like with a few restrictions: * The `include` folder and `node_modules` folder must be in a place that is visible to both the client and server * All `"$path"` fields should be relative to your `outDir` (which is the `"out"` folder by default) By default, the `default.project.json` file should look something like this (truncated): { "name": "roblox-ts-game", "tree": { "$className": "DataModel", "ServerScriptService": { "$className": "ServerScriptService", "TS": { "$path": "out/server" // server folder goes in ServerScriptService.TS } }, "ReplicatedStorage": { "$className": "ReplicatedStorage", // this _must_ stay the same (except for the name) "rbxts_include": { "$path": "include", "node_modules": { "$path": "node_modules/@rbxts" } }, "TS": { "$path": "out/shared" // shared folder goes in ReplicatedStorage.TS } }, "StarterPlayer": { "$className": "StarterPlayer", "StarterPlayerScripts": { "$className": "StarterPlayerScripts", "TS": { "$path": "out/client" // client folder goes in StarterPlayer.StarterPlayerScripts.TS } } } }} [You can find the full version here.](https://github.com/roblox-ts/create-roblox-ts/blob/master/templates/game/default.project.json) ### Example[​](https://roblox-ts.com/docs/guides/syncing-with-rojo#example "Direct link to Example") Suppose you wanted to add scripts to `StarterPlayer.StarterCharacterScripts`. To do this, we'll need to add a folder to `src` (so that when we compile it will have a matching folder in `out`). We'll call this `src/character`. Then, we need to update our `default.project.json`: "StarterPlayer": { "$className": "StarterPlayer", "StarterPlayerScripts": { "$className": "StarterPlayerScripts", "TS": { "$path": "out/client" } }, "StarterCharacterScripts": { "$className": "StarterCharacterScripts", "TS": { "$path": "out/character" } }} * [Introduction](https://roblox-ts.com/docs/guides/syncing-with-rojo#introduction) * [Customization](https://roblox-ts.com/docs/guides/syncing-with-rojo#customization) * [Example](https://roblox-ts.com/docs/guides/syncing-with-rojo#example) --- # Roact JSX | roblox-ts [Skip to main content](https://roblox-ts.com/docs/guides/roact-jsx#__docusaurus_skipToContent_fallback) On this page note The following guide assumes that you are already familiar with Roact. Please refer to the [Roact documentation](https://roblox.github.io/roact/) for more information. Introduction[​](https://roblox-ts.com/docs/guides/roact-jsx#introduction "Direct link to Introduction") -------------------------------------------------------------------------------------------------------- While roblox-ts allows you to use Roact just like you would in Luau, it also supports a "JSX" shorthand form. * JSX * Normal import Roact from "@rbxts/roact";const element = ( ); import Roact from "@rbxts/roact";const element = Roact.createElement( "Frame", { Size: new UDim2(1, 0, 1, 0), }, { Child: Roact.createElement("Frame", { Size: new UDim2(1, 0, 1, 0), }), }); [You can learn more about JSX here.](https://reactjs.org/docs/introducing-jsx.html) Tag Names[​](https://roblox-ts.com/docs/guides/roact-jsx#tag-names "Direct link to Tag Names") ----------------------------------------------------------------------------------------------- The "tag name" in JSX is the expression after the initial `<` character. For example, `frame` is the tag name of ``. You can use any Roblox UI class (host components) as a built-in JSX tag name by converting the name to lowercase. * `Frame` → `frame` * `UIListLayout` → `uilistlayout` * `ViewportFrame` → `viewportframe` * etc. Tag names can also be custom class components or functional components. **Custom components must use PascalCase.** import Roact from "@rbxts/roact";interface MyComponentProps { value: string;}function MyComponent(props: MyComponentProps) { return ;}const element = ; Tag names can also be a property access expression to use components which are nested inside of objects or namespaces. import Roact from "@rbxts/roact";interface MyComponentProps { value: string;}function MyComponent(props: MyComponentProps) { return ;}const Components = { MyComponent: MyComponent,};const element = ; Special Attributes[​](https://roblox-ts.com/docs/guides/roact-jsx#special-attributes "Direct link to Special Attributes") -------------------------------------------------------------------------------------------------------------------------- ### `Key` Attribute[​](https://roblox-ts.com/docs/guides/roact-jsx#key-attribute "Direct link to key-attribute") The `Key` attribute controls what your UI Instance will be named in the DataModel. This is the same as the `"Child"` key in this Luau example: Roact.createElement("Frame", { Child = Roact.createElement("Frame", {}),}) import Roact from "@rbxts/roact";const element = ( ); If an element is given the `Key` attribute and it **not** a child of another element, it will be wrapped in a `Roact.Fragment`. import Roact from "@rbxts/roact";const element = ; ### `Ref` Attribute[​](https://roblox-ts.com/docs/guides/roact-jsx#ref-attribute "Direct link to ref-attribute") The `Ref` attribute directly maps to the `[Roact.Ref]` key in Luau. import Roact from "@rbxts/roact";const ref = Roact.createRef();const element = ; ### `Change` Attribute[​](https://roblox-ts.com/docs/guides/roact-jsx#change-attribute "Direct link to change-attribute") The `Change` attribute takes an object which maps property name -> changed function. The changed function value will be given a reference `rbx` to the rendered UI instance. **Note the double curly braces `{{` and `}}`.** import Roact from "@rbxts/roact";const element = ( print(`${rbx.GetFullName()} changed Position!`), }} />); ### `Event` Attribute[​](https://roblox-ts.com/docs/guides/roact-jsx#event-attribute "Direct link to event-attribute") The `Event` attribute takes an object which maps property name -> event connection function. The event connection function value will be given a reference `rbx` to the rendered UI instance followed by the rest of the event arguments. **Note the double curly braces `{{` and `}}`.** import Roact from "@rbxts/roact";const element = ( print(`${rbx.GetFullName()} was clicked at (${x}, ${y})`), }} />); Spreading into Attributes[​](https://roblox-ts.com/docs/guides/roact-jsx#spreading-into-attributes "Direct link to Spreading into Attributes") ----------------------------------------------------------------------------------------------------------------------------------------------- You can spread objects into attributes using the form `{...exp}` where `exp` is an object. This is useful for creating reusable preset lists of properties. import Roact from "@rbxts/roact";const MyStyle: Partial> = { BackgroundColor3: new Color3(0, 0, 0), BackgroundTransparency: 0.5,};const element = ; Spreading into Children[​](https://roblox-ts.com/docs/guides/roact-jsx#spreading-into-children "Direct link to Spreading into Children") ----------------------------------------------------------------------------------------------------------------------------------------- You can spread arrays into children using the form `{...exp}` where `exp` is a `ReadonlyArray`. import Roact from "@rbxts/roact";const listItems = new Array();for (let i = 0; i < 10; i++) { listItems.push();}const element = {...listItems}; Using Values as Children[​](https://roblox-ts.com/docs/guides/roact-jsx#using-values-as-children "Direct link to Using Values as Children") -------------------------------------------------------------------------------------------------------------------------------------------- You can use values for children using the form `{exp}`. This is useful for programmatically creating children. The allowed values are: * `Roact.Element` * `ReadonlyArray` * `ReadonlyMap` * `boolean` * `undefined` `boolean` and `undefined` values do not actually get put into the children props, but allowing the values here is useful for creating conditional children values. import Roact from "@rbxts/roact";let condition = false;const element = ( {condition && } {condition ? : undefined} ); Fragments[​](https://roblox-ts.com/docs/guides/roact-jsx#fragments "Direct link to Fragments") ----------------------------------------------------------------------------------------------- To create a Fragment with JSX you can either use the tag name `Roact.Fragment` import Roact from "@rbxts/roact";const fragment = ; Or, you can use the shorthand form: import Roact from "@rbxts/roact";const fragment = <>; Extending the default JSX elements[​](https://roblox-ts.com/docs/guides/roact-jsx#extending-the-default-jsx-elements "Direct link to Extending the default JSX elements") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- By default the JSX supports all gui objects, however this may be limiting in cases where you want to manage other instances using Roact. Which elements are supported by JSX is determined by a global `JSX` namespace, to allow for more instances to be created this way you need to extend this global namespace. It is recommended to define this extension of the namespace in one central place for all instances. **Note the `JSX.IntrinsicElement` expects the type for an instance to be passed into it to allow for the properties & events to be typed correctly.** **Also note that, by convention, all Roblox instances should be lowercased.** declare global { namespace JSX { interface IntrinsicElements { // Your instances into here proximityprompt: JSX.IntrinsicElement; folder: JSX.IntrinsicElement; } }} * [Introduction](https://roblox-ts.com/docs/guides/roact-jsx#introduction) * [Tag Names](https://roblox-ts.com/docs/guides/roact-jsx#tag-names) * [Special Attributes](https://roblox-ts.com/docs/guides/roact-jsx#special-attributes) * [`Key` Attribute](https://roblox-ts.com/docs/guides/roact-jsx#key-attribute) * [`Ref` Attribute](https://roblox-ts.com/docs/guides/roact-jsx#ref-attribute) * [`Change` Attribute](https://roblox-ts.com/docs/guides/roact-jsx#change-attribute) * [`Event` Attribute](https://roblox-ts.com/docs/guides/roact-jsx#event-attribute) * [Spreading into Attributes](https://roblox-ts.com/docs/guides/roact-jsx#spreading-into-attributes) * [Spreading into Children](https://roblox-ts.com/docs/guides/roact-jsx#spreading-into-children) * [Using Values as Children](https://roblox-ts.com/docs/guides/roact-jsx#using-values-as-children) * [Fragments](https://roblox-ts.com/docs/guides/roact-jsx#fragments) * [Extending the default JSX elements](https://roblox-ts.com/docs/guides/roact-jsx#extending-the-default-jsx-elements) ---