Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>tiluaNew to Visual Studio Code? Get it now.
tilua

tilua

uav1010

| (0) | Free
tilua language support — diagnostics, hover, completion, go-to-definition
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

tilua

Luau, written the TypeScript way.

tilua compiles to Luau and keeps Luau's semantics: the same values, the same nil, the same 1-based arrays, the same metatables, the same runtime. What changes is the surface. Where Lua and TypeScript spell the same idea differently, tilua takes TypeScript's spelling — and the type system that comes with it. A Roblox script does what it always did; it just reads like .ts, and the editor catches what TypeScript would catch.

This extension gives you diagnostics, hover, completion, go-to-definition, find-references, rename, signature help and semantic highlighting for .tilua files. Everything it shows comes from a language server bundled inside it, so there is nothing to install and no path to configure.

Getting started

Install the extension, then, in your project:

npm i -D @tilua/compiler @tilua-types/roblox
// tilua.config.json — at the root of your project
{
  "types": ["roblox"],                          // brings @tilua-types/lua along
  "paths": { "@shared/*": ["src/shared/*"] },   // import aliases, as in tsconfig
  "sourceMap": null                             // or a Rojo sourcemap
}
// src/main.tilua
const players = game:GetService("Players")

function greet(player: Player): string {
    return `hello ${player.Name}`
}

players.PlayerAdded:Connect(player => print(greet(player)))
npx tilua src/main.tilua --out build/main.luau

The extension validates tilua.config.json as you type it, and the server reloads when it changes.

No globals are built in — not print, not game. A project lists the type libraries it wants, the way TypeScript uses @types/*:

library what it declares
@tilua-types/lua the Lua 5.1 standard library, and the methods arrays and strings answer to
@tilua-types/roblox Luau and the whole Roblox API — every class, enum, data type, service and global. Brings lua along
@tilua-types/sunc the script-executor environment: getgenv, writefile, hookfunction, Drawing, …

The language, at a glance

Everything not listed here is Luau as you know it: and / or / not, nil, .., ~=, #x, -- comments, : method calls, 1-based indexes, pairs / ipairs, elseif, repeat … until, continue.

Blocks — braces, and a parenthesised condition

Luau tilua
if x then … end if (x) { … }
elseif y then } elseif (y) {
while x do … end while (x) { … }
for i = 1, 10 do … end for (i = 1, 10) { … }
for k, v in pairs(t) do … end for (k, v in pairs(t)) { … }
repeat … until x repeat { … } until (x)
do … end do { … }
function f() … end function f() { … }

There is no end, and then is not a word tilua knows — both are still reserved, so using one is an error rather than a silent identifier.

The parentheses around a condition are not decoration: f {} is a call with a table argument, so without them if ready { … } would read as a call of ready followed by a block.

A single statement may stand in for the block, as TypeScript writes it:

if (done) return
for (_, item in items) if (item) print(item)

Declarations — const / let, no local

local is gone. const and let are hard keywords; function name() { … } declares name in the enclosing scope and cannot be reassigned, the way a TypeScript function declaration cannot. Function declarations hoist: a name is visible to its whole block, above itself too.

{} and [] mean different things

Luau writes every table with {}. tilua splits them, as JavaScript does:

Luau tilua
object { x = 1, y = 2 } { x: 1, y: 2 }
array { 1, 2, 3 } [1, 2, 3]
computed key { [k] = v } { [k]: v }
shorthand — { x, y }
spread — { ...base, c: 3 }, [...a, ...b]

Arrays are still Luau arrays underneath — the first element is index 1.

One arrow, for the type and for the function

Luau writes a function type with ->. tilua writes both a function type and a function value with =>, and decides which from where it stands, since a type and a value never share a place. -> is gone.

type Reducer = (total: number, value: number) => number

const double = (x: number) => x * 2
const add: Reducer = (a, b) => a + b        -- typed by the contract
each(n => print(n))                         -- one parameter needs no parens
const wrap = (n: number) => ({ value: n })  -- an object body is parenthesised

An arrow is just a short function expression — there is no second kind of function — so this inside one is the this of the method around it.

Optionality — no T?

? in type position always belongs to a conditional type, and in expression position to a ternary or an optional chain. Optionality is TypeScript's:

name?: T        -- may be absent; its type is `T | nil`
name: T | nil   -- must be written, but may be nil

Omitting an argument requires ? or a default — a parameter typed T | nil still has to be passed something.

Things Luau does not have at all

  • Classes — class Dog extends Animal { … }, constructor, get / set, static, super, new Dog(…), generic class Box<T>. It is sugar over the usual metatable idiom, and new Dog(x) is Dog.new(x).
  • Modules — import / export instead of require, including import type, re-exports and export default. Imports are read-only.
  • Optional chaining — a?.b and a?:m(x), which stop the whole chain when the receiver is nil, and narrow what they tested.
  • Ternary — c ? a : b.
  • Template strings — `hello ${name}`.
  • Rest and spread — ...parts: string[] in a signature, f(...names) at a call. Bare ... is still Lua's pack.
  • Array and string methods — names:filter(…):map(…), text:trim(), written with : as JavaScript writes them with .. Which methods exist comes from the type library, not the language.

A real type system

This is the part that shows up as squiggles. tilua checks what TypeScript checks, not what Luau checks:

  • strictNullChecks always on — reading a member of a possibly-nil value is an error until a check narrows the nil away.
  • TypeScript's narrowing model: references (x.a.b) rather than just variables, discriminated unions, and / or, early return, break, user type guards (v is T), assertion signatures (asserts v).
  • Unions, intersections, tuples, keyof, T[K], conditional types with infer, mapped types, template literal types, generics with constraints and defaults, satisfies, as const, overload sets, branded types.
  • Only nil and false are falsy — 0 and "" are truthy, as in Lua, not as in JavaScript.

Three comments switch checking off where you need it, as TypeScript's // @ts-… do:

--@tilua-nocheck          -- before the first line of code: the whole file
--@tilua-ignore           -- the next line of code
--@tilua-expect-error     -- the next line of code, which must have an error

Full language reference: the @tilua/parser README.

Settings

setting what it does
tilua.server.path absolute path to a cli.js / server.cjs to use instead of the bundled server
tilua.trace.server messages / verbose logs LSP traffic to the tilua output channel

tilua: Restart Language Server restarts the server without reloading the window.

Other editors

Every feature here is the language server's, so anything you see works the same anywhere else that speaks LSP. Install @tilua/language-server, launch tilua-language-server --stdio (or --node-ipc), and attach it to the tilua language / .tilua files.

Development

npm install
npm run build

Open this folder in VS Code and press F5. A second window opens on sample/, with hello.tilua to poke at. After changing the server, rebuild and run tilua: Restart Language Server in the dev window; to debug the server itself, run the Attach to server launch configuration (port 6009).

npm run reinstall packages a .vsix and installs it into your own VS Code — that is also the file to hand someone else. npm run publish publishes to the Marketplace, which needs a publisher id in package.json and npx vsce login <publisher-id> once.

  • src/extension.ts — locate the server, start it, register the restart command
  • syntaxes/tilua.tmLanguage.json — TextMate grammar (colours only; the server does the understanding)
  • language-configuration.json — comments, brackets, indentation
  • sample/ — the folder the dev host opens
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft