You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/advanced/writing-declarations.md
+14-14Lines changed: 14 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,19 +2,19 @@
2
2
title: Writing Declarations
3
3
---
4
4
5
-
The real power of the transpiler is unlocked when combining it with declarations for your target environment. Declarations tell TypeScript which Lua API is available in your target context.
5
+
The best way to use TypeScript is to provide it with information about the format/types of the external functions and variables that you will be using (specific to your environment). This allows the compiler to check your code for mistakes when compiling, instead of having to run the code to find issues. To give TypeScript this information, you will need to provide it with type declarations. You can write these declarations yourself or, if available, install an existing type declarations package for your environment from npm.
6
6
7
-
If you need tips or help writing declarations, feel free to [join our Discord](https://discord.gg/BWAq58Y).
7
+
For more information about installing existing type definition packages, see the [getting started page](getting-started.md#type-declarations).
8
8
9
-
## About Declaration Files
9
+
This page has more information about how to write your own type declarations. This can be tricky, so if you need help, feel free to [join our Discord server](https://discord.gg/BWAq58Y).
10
10
11
-
Declaration files end with the extension _.d.ts_. These contain pure ambient code.
11
+
## About Declaration Files
12
12
13
-
For TypeScriptToLua, these files should contain information that describes the target Lua environment.
13
+
Declaration files end with the extension `.d.ts` (which stands for "declaration TypeScript file"). Declaration files are different from normal `.ts` files in that they must only contain _ambient_ code. In the context of TypeScript, _ambient_ refers to code that only exists at compile-time and is not emitted into the program output.
14
14
15
-
This means functions, modules, variables and other members of the target Lua environment are primarily described in these files.
15
+
In other words, anything you put into a `.d.ts` file will inform the TypeScript compiler about what the format of something is. And it will never appear in the generated `.lua` file(s).
16
16
17
-
They don't contain code that you would execute. Similar to how you'd write an interface in some other languages. TypeScriptToLua doesn't output any information from these files either.
17
+
For TypeScriptToLua, these files should contain information that describes the target Lua environment. This means functions, modules, variables and other members of the target Lua environment are primarily described in these files.
18
18
19
19
:::note
20
20
You can write ambient declarations inside _.ts_ files as well.
@@ -38,7 +38,7 @@ declare const _VERSION: number;
38
38
39
39
/**
40
40
* Receives any number of arguments, and prints their values to stdout, using the
41
-
* tostring function to convert them to strings. print is not intended for
41
+
* `tostring` function to convert them to strings. print is not intended for
42
42
* formatted output, but only as a quick way to show a value, typically for
43
43
* debugging. For formatted output, use string.format.
44
44
* @paramargs Arguments to print
@@ -120,7 +120,7 @@ This allows users to modify `this` inside a function and expect behaviour simila
120
120
121
121
But obviously Lua does not have a `self` parameter for every function, so one of the three options must happen to tell TypeScriptToLua there is no "contextual parameter" (`self`):
122
122
123
-
1. Use `this: void` as the first parameter of the function / method. This formally describes to TypeScript to not allow `this` to be modified inside this function. (you could also use the [noImplicitThis](../configuration.md#custom-options) option to disallow `this` to be modified if `this` is of an `any` type).
123
+
1. Use `this: void` as the first parameter of the function / method. This formally describes to TypeScript to not allow `this` to be modified inside this function. (you could also use the [noImplicitThis](configuration.md#custom-options) option to disallow `this` to be modified if `this` is of an `any` type).
124
124
2. Use `@noSelf` in the comments of the declaration's owner (the namespace, module, object, etc).
125
125
3. Use `@noSelfInFile` at the beginning of the file in a comment to make sure every function defined in this file does not use a "contextual parameter".
126
126
@@ -172,7 +172,7 @@ Here are some commonly used TSDoc tags used in comments:
172
172
|`@param <name> <description>`| Defines a parameter. e.g. A parameter for a function |
173
173
|`@return <description>`| Describes the return value of a function / method |
174
174
175
-
TypeScriptToLua takes this further. Some "tags" change how the transpiler translates certain pieces of code. These are referred to as [annotations](compiler-annotations.md).
175
+
TypeScriptToLua takes this further. Some "tags" change how the transpiler translates certain pieces of code. These are referred to as [annotations](advanced/compiler-annotations.md).
176
176
177
177
As an example, `@tupleReturn` marks a function as something which returns multiple values instead of its array.
178
178
@@ -197,7 +197,7 @@ let [c, d] = array();
197
197
// local c, d = unpack(array())
198
198
```
199
199
200
-
See [Compiler Annotations](compiler-annotations.md) page for more information.
200
+
See [Compiler Annotations](advanced/compiler-annotations.md) page for more information.
201
201
202
202
## Environmental Declarations
203
203
@@ -223,7 +223,7 @@ We recommend reading about Mapped and Conditional types. These things can be use
Declaration merging is a feature of TypeScript that allows you to combine new declarations with ones that already exist. For more information, see [the TypeScript documentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html).
227
227
228
228
Some examples of declaration merging have been shown in the above examples.
The second option was added in version [0.38.0](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/CHANGELOG.md#0380). You can now use [language extensions](https://typescripttolua.github.io/docs/advanced/language-extensions) that allow declaring special functions which will transpile to operators. This will be completely type safe if the operators are declared correctly. See [Operator Map Types](language-extensions.md#operator-map-types) for more information.
535
+
The second option was added in version [0.38.0](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/CHANGELOG.md#0380). You can now use [language extensions](https://typescripttolua.github.io/docs/advanced/language-extensions) that allow declaring special functions which will transpile to operators. This will be completely type safe if the operators are declared correctly. See [Operator Map Types](advanced/language-extensions.md#operator-map-types) for more information.
536
536
537
537
### Import and export
538
538
@@ -555,7 +555,7 @@ declare module "mymodule" {
555
555
}
556
556
```
557
557
558
-
## NPM Publishing
558
+
## npm Publishing
559
559
560
560
It is possible to publish a list of declarations for other users to easily download via [npm](https://www.npmjs.com/).
Copy file name to clipboardExpand all lines: docs/caveats.md
+55-18Lines changed: 55 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,10 @@
2
2
title: Caveats
3
3
---
4
4
5
+
Luckily, for most use-cases, you can write modern, idiomatic TypeScript, and TSTL will produce transpiled Lua that will work flawlessly. In other words, you probably will not have to worry about the idiomatic quirks of Lua or the internal decisions that TSTL makes when converting code.
6
+
7
+
With that said, TSTL does have some "gotchas" that you might run into. This page covers some of those edge-cases.
This project aims for both compilation results to have the same behavior as much as possible, but not at all costs. Since TypeScript is based on JavaScript it also inherited some of the quirks in JavaScript that are not present in Lua. This is where behavior between Lua and JavaScript compilation targets diverge. TypeScriptToLua aims to keep identical behavior as long as **sane** TypeScript is used: if JavaScript-specific quirks are used behavior might differ.
24
+
This project aims for both compilation results to have the same behavior as much as possible, but not at all costs. Since TypeScript is based on JavaScript, it also inherited some of the quirks in JavaScript that are not present in Lua. This is where behavior between Lua and JavaScript compilation targets diverge. TypeScriptToLua aims to keep identical behavior as long as **sane** TypeScript is used: if JavaScript-specific quirks are used, behavior might differ.
21
25
22
26
Below are some of the cases where resulting Lua intentionally behaves different from compiled JS.
23
27
@@ -39,49 +43,82 @@ JavaScript and Lua differ in what they evaluate to true/false. TypeScriptToLua a
39
43
|`0`|`false`| ⚠️`true`|
40
44
| (Everything else) |`true`|`true`|
41
45
46
+
We recommend that you use the [`strict-boolean-expression`](https://typescript-eslint.io/rules/strict-boolean-expressions/) ESLint rule in your TSTL projects, which will force you to be explicit and prevent this class of bug entirely.
TypeScriptToLua makes no difference between `==` and `===` when compiling to Lua, treating all comparisons as strict (`===`).
45
51
52
+
We recommend that you use the [`eqeqeq`](https://eslint.org/docs/latest/rules/eqeqeq) ESLint rule, which will force you to be explicit and prevent this class of bug entirely.
53
+
54
+
### `undefined` and `null`
55
+
56
+
`nil` is the Lua equivalent for `undefined`, so TSTL converts `undefined` to `nil`. However, there is no Lua equivlanet for `null`, so TSTL converts `null` to `nil` as well.
57
+
58
+
This means that TSTL programs with `null` will have different behavior than JavaScript/TypeScript programs. For example:
59
+
60
+
```ts
61
+
const foo = {
62
+
someProp1: 123,
63
+
someProp2: null,
64
+
someProp3: undefined,
65
+
};
66
+
```
67
+
68
+
If we iterated over `foo` in a TSTL program, we would _only_ get `someProp1`, instead of both `someProp1` and `someProp2` like we would in a JavaScript/TypeScript program.
69
+
70
+
In general, we recommend keeping `null` out of your TSTL codebases in favor of `undefined`. Not only will this represent the transpiled Lua code better, but [it is more idiomatic in TypeScript to prefer `undefined` over `null` when both would accomplish the same thing](https://basarat.gitbook.io/typescript/recap/null-undefined).
71
+
46
72
### Array Length
47
73
48
-
`Array.prototype.length` is translated to Lua's `#` operator. Due to the way lists are implemented in Lua there can be differences between JavaScript's `list.length` and Lua's `#list`. The transpiler does not do anything to remedy these differences, so when working with lists, the transpiled Lua will use the standard Lua conventions. Generally speaking, the situation where these differences occur happen when adding/removing items to a list in a hacky way, or when setting list items to `undefined`/`null`.
74
+
`Array.prototype.length` is translated to Lua's `#` operator. Due to the way arrays are implemented in Lua, there can be differences between JavaScript's `myArray.length` and Lua's `#myArray`. The transpiler does not do anything to remedy these differences. Thus, when working with arrays, the transpiled Lua will use the standard Lua conventions. Generally speaking, the situation where these differences occur happen when adding/removing items to an array in a hacky way, or when setting array items to `undefined` / `null`.
49
75
50
-
**Examples:**
76
+
For example:
51
77
52
-
**Safe (no difference):**
78
+
#### Safe (no difference)
53
79
54
80
```ts
55
-
constmyList= [1, 2, 3];
56
-
myList.push(4);
57
-
myList.pop();
58
-
myList.splice(1, 1);
59
-
//myList.length == 2
81
+
constmyArray= [1, 2, 3];
82
+
myArray.push(4);
83
+
myArray.pop();
84
+
myArray.splice(1, 1);
85
+
//myArray.length == 2
60
86
```
61
87
62
-
**Differences might occur:**
88
+
#### Differences might occur
63
89
64
90
```ts
65
-
constmyList= [1, 2, 3];
66
-
myList[1] =undefined;
67
-
//myList.length == 1 (3 in JavaScript)
91
+
constmyArray= [1, 2, 3];
92
+
myArray[1] =undefined;
93
+
//myArray.length == 1 (which would be 3 in JavaScript)
68
94
```
69
95
70
96
```ts
71
-
constmyList= [1, 2, 3];
72
-
myList[4] =5;
73
-
//myList.length == 3 (5 in JavaScript)
97
+
constmyArray= [1, 2, 3];
98
+
myArray[4] =5;
99
+
//myArray.length == 3 (which would be 5 in JavaScript)
74
100
```
75
101
76
102
### Key Iteration Order
77
103
78
104
Even though iterating over object keys with `for ... in` does not guarantee order in either JavaScript or Lua. Therefore, the iteration order in JavaScript is likely different from the order in Lua.
79
105
80
-
**Note:** If a specific order is required, it is better to use ordered collections like lists instead.
106
+
**Note:** If a specific order is required, it is better to use ordered collections like arrays instead.
81
107
82
108
### Iterating an array with `for ... in`
83
109
84
-
Not allowed.
110
+
Not allowed. Use a `for of` loop instead to iterate over an array.
111
+
112
+
### Sorting
113
+
114
+
A sorting algorithm is [said to be stable](https://stackoverflow.com/questions/1517793/what-is-stability-in-sorting-algorithms-and-why-is-it-important) if two objects with equal keys appear in the same order in sorted output as they appear in the input array to be sorted.
115
+
116
+
- Sorting is part of the JavaScript standard library via the `Array.sort` method. It is guaraunteed to be [stable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
117
+
- Sorting is also part of the Lua standard library via the `table.sort` method. It is **not** guaraunteed to be [stable](https://www.lua.org/manual/5.3/manual.html#pdf-table.sort).
118
+
119
+
TypeScriptToLua relies on the Lua standard library for sorting. In other words, it transpiles `[1, 2, 3].sort();` to `table.sort({1, 2, 3})`. So beware that your sorts will no longer be stable!
120
+
121
+
If you need stable sorting, you have to manually use a custom sorting function. For some examples of this, see the [sorting helper functions from `isaacscript-common`](https://github.com/IsaacScript/isaacscript/blob/main/packages/isaacscript-common/src/functions/sort.ts).
In your `tstl` project, you might want to import some existing Lua code. Or, you might want to import a library from [npm](https://www.npmjs.com/). This page describes how to use external code.
6
+
7
+
:::note
8
+
This page is about importing code that **actually executes something**. In a `tstl` project, it is common to depend on external library that provide type declarations. Type declaration libraries only provide types: they do not contribute any code to your actual program output. Thus, they work a little differently from what is discussed on this page. For information on how type declarations work, see the [type declarations page](advanced/writing-declarations.md).
9
+
:::
10
+
11
+
## Adding Lua files to your project sources
12
+
13
+
The most straightforward way to add Lua code is to put the Lua file directly next to your TypeScript files. Next, you add [a declaration file](advanced/writing-declarations.md) with the same name. Then, you can import the Lua code in your TypeScript.
14
+
15
+
For example, a project might look like this:
16
+
17
+
```text
18
+
project/
19
+
├── main.ts
20
+
├── someLua.lua
21
+
├── someLua.d.ts
22
+
└── tsconfig.json
23
+
```
24
+
25
+
```ts title=main.ts
26
+
import { foo, bar } from"./someLua";
27
+
28
+
foo();
29
+
bar();
30
+
```
31
+
32
+
```lua title=someLua.lua
33
+
localsomeLua= {}
34
+
35
+
functionsomeLua:foo()
36
+
print("hello")
37
+
end
38
+
39
+
functionsomeLua:bar()
40
+
print("world")
41
+
end
42
+
43
+
returnsomeLua
44
+
```
45
+
46
+
```ts title=someLua.d.ts
47
+
exportfunction foo():void;
48
+
exportfunction bar():void;
49
+
```
50
+
51
+
## Importing a Lua module that only exports an array
52
+
53
+
Building on the previous section, you might want also want to import a Lua file that exports an array. For example, something like:
54
+
55
+
```lua title=things.lua
56
+
return {
57
+
{
58
+
foo=123,
59
+
bar=456,
60
+
},
61
+
{
62
+
foo=789,
63
+
bar=987,
64
+
},
65
+
}
66
+
```
67
+
68
+
Writing a definitions file for this is tricky, since the Lua file has no named imports and no default export. Here, you have to use `export =` syntax, like so:
69
+
70
+
```ts title=things.d.ts
71
+
interfaceThing {
72
+
foo:number;
73
+
bar:number;
74
+
}
75
+
76
+
declareconst things:Thing[];
77
+
export=things;
78
+
```
79
+
80
+
Then, in your TypeScript code, you can import it like:
81
+
82
+
```ts title=main.ts
83
+
import*asthingsfrom"./module";
84
+
85
+
print(things[0].foo); // Prints "123"
86
+
```
87
+
88
+
For more information about this export syntax, see [the official TypeScript documentation](https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require).
89
+
90
+
## Importing Lua packages from npm
91
+
92
+
`tstl` supports module resolution for libraries, which means you can _use_ and _create_ npm packages containing `.lua` files. (Most packages on npm contain JavaScript files, but npm allows you to create packages with whatever kinds of files you want.)
93
+
94
+
### Using Lua packages
95
+
96
+
To use a Lua package, install it via npm and use it in the same way that you would in a normal TypeScript project. In other words:
97
+
98
+
```sh
99
+
# If you use npm:
100
+
npm install foo --save
101
+
102
+
# If you use yarn:
103
+
yarn add foo
104
+
105
+
# If you use pnpm:
106
+
pnpm add foo
107
+
```
108
+
109
+
And then use it in your code:
110
+
111
+
```ts
112
+
import { someFunction } from"foo";
113
+
114
+
someFunction();
115
+
```
116
+
117
+
Since the npm package was presumably made for `tstl` users, it will almost certainly include `.d.ts` files alongside the `.lua` files, which is necessary for `tstl` to import the Lua files properly. If there are no `.d.ts` files, you can try [creating some for the package yourself](advanced/writing-declarations.md).
118
+
119
+
### Creating Lua packages
120
+
121
+
For more information on creating a Lua package yourself, see [the page on publishing modules](publishing-modules.md).
122
+
123
+
## Importing JavaScript or TypeScript packages from npm
124
+
125
+
**Importing JavaScript or TypeScript packages from npm will not work.** This means that it is impossible to use common JavaScript/TypeScript libraries like [Underscore.js](https://underscorejs.org/) and so on.
126
+
127
+
(It is not possible for `tstl` to work with a generic npm package because most TypeScript libraries only publish the compiled JavaScript to npm. And `tstl` can't convert JavaScript to Lua because it needs the type information to create correct code. For example, `tstl` needs to be able to distinguish between arrays and objects in order to write the correct index.)
128
+
129
+
As a workaround, you can copy paste TypeScript code from a package repository directly into your project. (That way, it will be compiled by `tstl` alongside your normal code.)
130
+
131
+
Alternatively, you could fork an existing package and re-publish it as Lua files (instead of JavaScript) so that it can be directly consumed by other `tstl` projects. However, doing this kind of thing will only work for really basic packages, since you would have to also fork all of the dependencies and convert those to Lua as well.
132
+
133
+
Obviously, `tstl` programs will not have access to any of the Node.js standard libraries or APIs (like e.g. `import path from "path";`), so make sure that any code that you integrate into your project is not Node-specific.
0 commit comments