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"],                          // on top of Lua's standard library
  "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.

Lua's standard library is built in — print, pairs, string, math, pcall — as JavaScript's is in TypeScript ("noLib": true leaves it out). What a host adds, a project lists, the way TypeScript uses @types/*:

library what it declares
@tilua-types/roblox Luau and the whole Roblox API — every class, enum, data type, service and global
@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 (const [k, v] of 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 (const item of 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, generic class Box<T>, public / private / protected, readonly, abstract, override, implements, and metamethods (function __add(other: Vec): Vec). It is sugar over the usual metatable idiom: an instance is built with Dog.new(x), as in Luau.
  • Arrays instead of packs — a function returns one value, and several are an array, a table: return [a, b], const [a, b] = f(). A loop takes one value too — for (const [k, v] of pairs(t)); for (const key in t) walks the keys, as in TypeScript. Varargs are rest parameters (...args: T[]), the script's own are scriptArgs, and pcall answers { success, data, error }.
  • Modules — import / export instead of require, including import type, re-exports and export default. Imports are read-only.
  • Optional chaining — a?.b, a?.[k] and a?:m(x), which stop the whole chain when the receiver is nil, and narrow what they tested.
  • Ternary — c ? a : b.
  • TypeScript's operators — a ?? b (where a is nil; a false stays), n++ / n--, "key" in t, and v instanceof Dog for classes written in tilua.
  • Interfaces — interface B extends A { … }, merged across declarations, with method, call, constructor and get / set members; object literals take get / set too.
  • Template strings — `hello ${name}`.
  • Rest and spread — ...parts: string[] in a signature, f(...names) at a call, [...names, "z"] in an array.
  • 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.
  • strictFunctionTypes too — a callback has to take what its caller passes — and a let keeps the type it was first given, a name declared without a value is not read before it has one, and every way through a function that returns a value returns one.
  • What Luau raises on, said first: an operator's operands are checked (n * 2 with n a number | nil), as is a comparison the types already settle. A name is not readable above its declaration, a list is indexed by position, readonly T[] cannot be written to, a key written twice in one table is reported, and a parameter with no type says so.
  • 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), one-sided guards (implies 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, polymorphic this, in / out variance, NoInfer, InstanceType.
  • Only nil and false are falsy — 0 and "" are truthy, as in Lua, not as in JavaScript.

Four 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, errors and warnings
--@tilua-expect-error     -- the next line of code, which must have an error
--@tilua-expect-warning   -- the next line of code, which must have a warning

An expect- comment covers its own kind only: a warning neither answers an expect-error nor is hidden by one.

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.

Compatibility

tilua-vscode@9.1.4 works with these tilua packages. Released with is the version it was built and tested against.

Package Range Released with
@tilua/language-server ^9.1.4 9.1.4 dependency

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