Skip to content

Latest commit

 

History

History
667 lines (461 loc) · 12.8 KB

File metadata and controls

667 lines (461 loc) · 12.8 KB
title Compiler Annotations

import { SideBySide } from "@site/src/components/SideBySide";

To improve translation and compatibility to different Lua interfaces, the TypeScriptToLua transpiler supports several custom annotations that slightly change translation results. This page documents the supported annotations. The syntax of the compiler annotations use the JSDoc syntax.

@compileMembersOnly

Target elements: (declare) enum

This decorator removes an enumeration's name after compilation and only leaves its members. Primarily used for APIs with implicit enumerations.

Example

declare enum MyEnum {
  MY_ENUM_MEMBER_A,
  MY_ENUM_MEMBER_B,
}

print(MyEnum.MY_ENUM_MEMBER_A);
print(MyEnum.MY_ENUM_MEMBER_A)
/** @compileMembersOnly */
declare enum MyEnum {
  MY_ENUM_MEMBER_A,
  MY_ENUM_MEMBER_B,
}

print(MyEnum.MY_ENUM_MEMBER_A);
print(MY_ENUM_MEMBER_A)

Example 2

enum MyEnum {
  MY_ENUM_MEMBER_A,
  MY_ENUM_MEMBER_B,
  MY_ENUM_MEMBER_C = "c",
}

print(MyEnum.MY_ENUM_MEMBER_A);
MyEnum = {}
MyEnum.MY_ENUM_MEMBER_A = 0
MyEnum.MY_ENUM_MEMBER_B = 1
MyEnum.MY_ENUM_MEMBER_C = "c"

print(MyEnum.MY_ENUM_MEMBER_A)
/** @compileMembersOnly */
enum MyEnum {
  MY_ENUM_MEMBER_A,
  MY_ENUM_MEMBER_B,
  MY_ENUM_MEMBER_C = "c",
}

print(MyEnum.MY_ENUM_MEMBER_A);
MY_ENUM_MEMBER_A = 0
MY_ENUM_MEMBER_B = 1
MY_ENUM_MEMBER_C = "c"

print(MY_ENUM_MEMBER_A)

@customConstructor

Target elements: declare class

Changes the way new instances of this class are made. Takes exactly one argument that is the name of the alternative constructor function.

Example

declare class MyClass {
  constructor(x: number);
}
const inst = new MyClass(3);
local inst = __TS__New(MyClass, 3)
/** @customConstructor MyConstructor */
declare class MyClass {
  constructor(x: number);
}
const inst = new MyClass(3);
local inst = MyConstructor(3)

@extension

Target elements: class

The Extension decorator marks a class as an extension of an already existing class. This causes the class header to not be translated, preventing instantiation and the override of the existing class.

Example

class MyBaseClass {
  myFunction(): void {}
}
MyBaseClass = __TS__Class()
...
function MyBaseClass.prototype.myFunction(self) end
/** @extension */
class MyBaseClass {
  myFunction(): void {}
}
function MyBaseClass.myFunction(self) end

@forRange

Target elements: declare function

Denotes a function declaration is a Lua numerical iterator. When used in a TypeScript for...of loop, the resulting Lua will use a numerical for loop.

The function should not be a real function and an error will be thrown if it is used in any other way.

Example

/** @forRange */
declare function forRange(start: number, limit: number, step?: number): number[];

for (const i of forRange(1, 10)) {}
for (const i of forRange(10, 1, -1)) {}
for i = 1, 10 do end
for i = 10, 1, -1 do end

@luaIterator

Target elements: (declare) interface

Denotes a type is a Lua iterator. When an object of a type with this annotation is used in a for...of statement, it will transpile directly as a lua iterator in a for...in statement, instead of being treated as a TypeScript iterable. Typically, this is used on an interface that extends Iterable or Array so that TypeScript will allow it to be used in a for...of statement.

Example

/** @luaIterator */
type LuaIterable<T> = Iterable<T>;

declare function myIterator(): LuaIterator<string>;
for (const s of myIterator()) {}
for s in myIterator() do end

This can also be combined with @tupleReturn, if the iterator returns multiple values.

Example

/** @luaIterator @tupleReturn */
type LuaTupleIterable<T extends any[]> = Iterable<T>;

declare namespace string {
  function gmatch(s: string, pattern: string): LuaTupleIterable<string[]>;
}

for (const [a, b] of string.gmatch("foo", "(.)(.)")) {}
for a, b in string.gmatch("foo", "(.)(.)") do end

@luaTable

Target elements: type

This annotation signals the transpiler to translate a class as a simple lua table for optimization purposes.

/** @luaTable */
declare class Table<K extends {} = {}, V = any> {
  readonly length: number;
  set(key: K, value: V | undefined): void;
  get(key: K): V | undefined;
}

const tbl = new Table(); // local tbl = {}

const foo = {};
tbl.set(foo, "bar"); // tbl[foo] = "bar"
print(tbl.get(foo)); // print(tbl[foo])

tbl.set(1, "baz"); // tbl[1] = "baz"
print(tbl.length); // print(#tbl)

@metaExtension

Target elements: class

The Extension decorator marks a class as an extension of an already existing meta class/table. This causes the class header to not be translated, preventing instantiation and the override of the existing class.

Example

class MyBaseClass {
  myFunction(): void {}
}
MyBaseClass = __TS__Class()
...
function MyBaseClass.prototype.myFunction(self) end
/** @metaExtension */
class MyMetaExtension extends MyMetaClass {
  myFunction(): void {}
}
local __meta__MyMetaClass = debug.getregistry().MyMetaClass
__meta__MyMetaClass.myFunction = function(self)
end;

@noResolution

Target elements: module

Prevents tstl from trying to resolve the module path. When importing this module the path will be exactly the path in the import statement.

Example

declare module "mymodule" {}
import module from "mymodule";
...
local module = require("src.mymodule");
/** @noResolution */
declare module "mymodule" {}
import module from "mymodule";
...
local module = require("mymodule");

@noSelf

Target elements: declare class, (declare) interface or declare namespace

Indicates that functions inside a scope do not take in initial self argument when called, and thus will be called with a dot . instead of a colon :. It is the same as if each function was declared with an explicit this: void parameter. Functions that already have an explicit this parameter will not be affected.

When applied to a class or interface, this only affects the type's declared methods (including static methods and fields with a function type). It will not affect other function declarations, such as nested functions inside a class' methods.

Example

declare interface NormalInterface {
  normalMethod(s: string): void;
}
declare const x: NormalInterface;

/** @noSelf **/
declare interface NoSelfInterface {
  noSelfMethod(s: string): void;
}
declare const y: NoSelfInterface;

x.normalMethod("foo");
y.noSelfMethod("bar");
x:normalMethod("foo")
y.noSelfMethod("bar")

When applied to a namespace, all functions declared within the namespace will treated as if they do not have a self parameter. In this case, the effect is recursive, so functions in nested namespaces and types declared as parameters will also be affected.

Example

declare namespace NormalNS {
  function normalFunc(s: string): string;
}

/** @noSelf **/
declare namespace NoSelfNS {
  function noSelfFunc(s: string): string;
}

NormalNS.normalFunc("foo");
NoSelfNS.noSelfFunc("bar");
NormalNS:normalFunc("foo")
NoSelfNS.noSelfFunc("bar")

For more information about how the self parameter is handled, see Functions and the self Parameter

@noSelfInFile

Target elements: (declare) file

Indicates that functions in a file do not take in initial self argument when called.

This is annotation works the same as @noSelf being applied to a namespace, but affects the entire file.

@noSelfInFile must be placed at the top of the file, before the first statement.

@phantom

Target elements: namespace

This decorator marks a namespace as a phantom namespace. This means all members of the namespace will be translated as if they were not in that namespace. Primarily used to prevent scoping issues.

Example

namespace myNameSpace {
  function myFunction(): void {}
}
myNameSpace = {}
function myNameSpace.myFunction() end
/** @phantom */
namespace myNameSpace {
  function myFunction(): void {}
}
function myFunction() end

@pureAbstract

Target elements: declare class

This decorator marks a class declaration as purely abstract. The result is that any class extending the purely abstract class will not extend this class in the resulting Lua.

Example

declare class MyAbstractClass {}
class MyClass extends MyAbstractClass {}
MyClass = __TS__Class()
MyClass.__base = MyAbstractClass
MyClass.____super = MyAbstractClass
setmetatable(MyClass, MyClass.____super)
setmetatable(MyClass.prototype, MyClass.____super.prototype)
/** @pureAbstract */
declare class MyAbstractClass {}
class MyClass extends MyAbstractClass {}
MyClass = __TS__Class()

@tupleReturn

Target elements: (declare) function

This decorator indicates a function returns a lua tuple instead of a table. It influences both destructing assignments of calls of that function, as well as changing the format of returns inside the function body.

Example

function myFunction(): [number, string] {
  return [3, "4"];
}
const [a, b] = myFunction();
function myFunction()
    return {3, "4"}
end
local a,b = unpack(myFunction())
/** @tupleReturn */
function myFunction(): [number, string] {
  return [3, "4"];
}
const [a, b] = myFunction();
function myFunction()
    return 3, "4"
end
local a, b = myFunction()

If you wish to use this annotation on function with overloads, it must be applied to each signature that requires it.

Example

/** @tupleReturn */
declare function myFunction(s: string): [string, string];
/** @tupleReturn */
declare function myFunction(n: number): [number, number];

Note that if any overloaded signature of a function implementation has the annotation, all array/tuple return values will unpacked in the transpiled output.

@vararg

Target elements: (declare) interface or type

Indicates that an array-like type represents a Lua vararg expression (...) and should be transpiled to that when used in a spread expression. This is useful for forwarding varargs instead of wrapping them in a table and unpacking them.

Example

function varargWrapUnpack(...args: string[]) {
  console.log(...args);
}
function varargWrapUnpack(self, ...)
    local args = ({...})
    print(unpack(args))
end
/** @vararg */
interface Vararg<T> extends Array<T> {}

function varargForward(...args: Vararg<string>) {
  console.log(...args);
}
function varargForward(self, ...)
    print(...))
end

This can be used to access the file-scope varargs as well.

Example

declare const arg: Vararg<string>;
console.log(...arg);
const [x, y] = [...arg];
print(...)
local x, y = ...

To also support tuple-typed rest parameters, you can define the type like this:

Example

/** @vararg */
type Vararg<T extends unknown[]> = T & { __luaVararg?: never };

function varargForward(...args: Vararg<[string, number]>) {}

Warning

TypeScriptToLua does not check that the vararg expression is valid in the context it is used. If the array is used in a spread operation in an invalid context (such as a nested function), a deoptimization will occur.

Example

function outerFunction(...args: Vararg<string>) {
  function innerFunction() {
    console.log(...args);
  }
  innerFunction();
}
function outerFunction(self, ...)
    local args = {...}
    local function innerFunction(self)
        print(unpack(args))
    end
    innerFunction(_G)
end