@tilua/parser 3.1.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +286 -39
- package/dist/index.cjs +3297 -502
- package/dist/index.d.cts +229 -42
- package/dist/index.d.ts +229 -42
- package/dist/index.js +3279 -501
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,13 +13,12 @@ the same one either way.
|
|
|
13
13
|
|---|---|
|
|
14
14
|
| [`@tilua/compiler`][compiler] | building a project to one Luau file |
|
|
15
15
|
| [`@tilua/language-server`][server] | editor support over LSP |
|
|
16
|
-
| [`@tilua-types/roblox`][roblox], [`@tilua-types/
|
|
16
|
+
| [`@tilua-types/roblox`][roblox], [`@tilua-types/sunc`][sunc] | the globals a project adds to Lua's |
|
|
17
17
|
| `@tilua/parser` | this: tokens, AST, scopes, types |
|
|
18
18
|
|
|
19
19
|
[compiler]: https://www.npmjs.com/package/@tilua/compiler
|
|
20
20
|
[server]: https://www.npmjs.com/package/@tilua/language-server
|
|
21
21
|
[roblox]: https://www.npmjs.com/package/@tilua-types/roblox
|
|
22
|
-
[lua]: https://www.npmjs.com/package/@tilua-types/lua
|
|
23
22
|
[sunc]: https://www.npmjs.com/package/@tilua-types/sunc
|
|
24
23
|
[vscode]: https://marketplace.visualstudio.com/search?term=tilua
|
|
25
24
|
|
|
@@ -75,19 +74,23 @@ Neither analysis mutates the AST; both return side tables.
|
|
|
75
74
|
|
|
76
75
|
## Projects
|
|
77
76
|
|
|
78
|
-
**
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
|
|
77
|
+
**Lua's standard library is built in**, as JavaScript's is in TypeScript:
|
|
78
|
+
`print`, `pairs`, `pcall`, `string`, `math`, `table`, `os`, `coroutine` and
|
|
79
|
+
`console` are there in every file with nothing listed (see
|
|
80
|
+
`STANDARD_LIBRARY_SOURCE`), and so are the language's own utility types
|
|
81
|
+
(`Partial`, `Pick`, `Omit`, `Record`, `ReturnType`, `Truthy`, ...; see
|
|
82
|
+
`PRELUDE_SOURCE`). What a host adds — Luau's and Roblox's `game`, `task`,
|
|
83
|
+
`Instance` — a project lists in `tilua.config.json`, the way TypeScript uses
|
|
84
|
+
`@types/*`:
|
|
82
85
|
|
|
83
86
|
```bash
|
|
84
|
-
npm i -D @tilua-types/roblox # Luau + Roblox
|
|
87
|
+
npm i -D @tilua-types/roblox # Luau + Roblox
|
|
85
88
|
```
|
|
86
89
|
|
|
87
90
|
```jsonc
|
|
88
91
|
// tilua.config.json
|
|
89
92
|
{
|
|
90
|
-
"types": ["roblox"], //
|
|
93
|
+
"types": ["roblox"], // on top of Lua's standard library
|
|
91
94
|
"paths": { "@shared/*": ["src/shared/*"] }, // import aliases, as in tsconfig
|
|
92
95
|
"sourceMap": "sourcemap.json" // a Rojo sourcemap, or null
|
|
93
96
|
}
|
|
@@ -103,7 +106,13 @@ npm i -D @tilua-types/roblox # Luau + Roblox; or @tilua-types/lua on its
|
|
|
103
106
|
matches every installed type library it fits (`"@tilua-types/*"`), in name
|
|
104
107
|
order. A relative path (`"./types"`, `"./defs.d.tilua"`) loads the project's
|
|
105
108
|
own definitions. A type library's own type-library dependencies load first;
|
|
106
|
-
one that is not installed is an error.
|
|
109
|
+
one that is not installed is an error. `"lua"` — `@tilua-types/lua`, where
|
|
110
|
+
the standard library used to be — loads nothing: it is already there, and
|
|
111
|
+
a library that still depends on it does not bring it in twice.
|
|
112
|
+
- **`noLib`** — `true` leaves the standard library out, as TypeScript's
|
|
113
|
+
`noLib` does: no `print`, no `string`, only what `types` declares. For a
|
|
114
|
+
host with none of Lua's, or a library of one's own in its place. With it,
|
|
115
|
+
`"lua"` in `types` loads that package as an ordinary library.
|
|
107
116
|
- **`paths`** — tsconfig rules: an exact pattern wins, then the `*` pattern
|
|
108
117
|
with the longest prefix; targets resolve from `baseUrl` (default: the
|
|
109
118
|
config's folder).
|
|
@@ -111,6 +120,11 @@ npm i -D @tilua-types/roblox # Luau + Roblox; or @tilua-types/lua on its
|
|
|
111
120
|
follow it, and a file the tree maps gets its own `script`, so
|
|
112
121
|
`script.Parent.Remotes` is typed. A `.tilua` file matches the Luau file of
|
|
113
122
|
the same path.
|
|
123
|
+
- **`target`** and **`output`** — the compiler's: which Lua to emit for
|
|
124
|
+
(`"luau"`, the default, or `"lua51"`), and how many files to write
|
|
125
|
+
(`"single"`, the default; `"chunks"`, which also writes a file for what only
|
|
126
|
+
an `import()` reaches; or `"modules"`, one file per module). The parser only
|
|
127
|
+
checks them.
|
|
114
128
|
|
|
115
129
|
| function | does |
|
|
116
130
|
|---|---|
|
|
@@ -118,20 +132,38 @@ npm i -D @tilua-types/roblox # Luau + Roblox; or @tilua-types/lua on its
|
|
|
118
132
|
| `loadConfig(path, host?)` | read and check one config |
|
|
119
133
|
| `resolveTypeLibraries(config, host?)` | the `.d.tilua` files to load, in order |
|
|
120
134
|
| `moduleCandidates(from, specifier, config?)` / `resolveModulePath(...)` | what an `import` means |
|
|
135
|
+
| `importExpressions(program)` | every `import("...")` written in a file |
|
|
121
136
|
| `sourceMapTypes(text, path, { classes })` | the tree's types, and `scriptFor(file)` |
|
|
122
137
|
|
|
123
138
|
Every problem comes back as `{ file, message, line, column }`, pointing into
|
|
124
139
|
the config (or sourcemap) it is about. `host` reads files — pass your own to
|
|
125
140
|
read unsaved editor buffers or to record what was read.
|
|
126
141
|
|
|
127
|
-
`
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
`typeof v` is the language's own, an operator like `not` and `#`: it needs no
|
|
143
|
+
library, and Lua's `type()` and Luau's `typeof()` functions are declared by
|
|
144
|
+
none. It answers Luau's names — `"nil"`, `"boolean"`, `"number"`, `"string"`,
|
|
145
|
+
`"function"`, `"thread"`, `"buffer"`, `"userdata"` — except for a table, which
|
|
146
|
+
is an `"array"` or an `"object"`. `typeof v == "array"` narrows `v`, and
|
|
147
|
+
`== "table"` is an error: it is never the answer. There is no `typeof(v)`
|
|
148
|
+
function: written that way it is an error, and a parenthesised operand stands
|
|
149
|
+
apart from the operator — `typeof (a or b)`.
|
|
150
|
+
|
|
151
|
+
What a runtime's own values answer is a library's to say, in one alias:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
type TypeofNames = { Vector3: Vector3, Instance: Instance }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Each property is the name `typeof` gives a value of the property's type; a
|
|
158
|
+
value several fit (a `Part` is an `Instance`) is the first one's.
|
|
159
|
+
`TypeAnalysis.typeofTables` lists each `typeof v` whose `v` is, by its type,
|
|
160
|
+
an array or an object wherever it is a table — what a compiler need not find
|
|
161
|
+
out as the program runs.
|
|
130
162
|
|
|
131
163
|
Libraries stack: a name declared again *adds* to what an earlier library gave
|
|
132
164
|
it — overloads of a function accumulate, and the members of a declared table
|
|
133
|
-
merge. That is how `@tilua-types/roblox` extends Lua's `table`
|
|
134
|
-
restating
|
|
165
|
+
merge. That is how `@tilua-types/roblox` extends Lua's `table` without
|
|
166
|
+
restating it.
|
|
135
167
|
|
|
136
168
|
## The language, in brief
|
|
137
169
|
|
|
@@ -219,6 +251,23 @@ is what a JavaScript arrow is for.
|
|
|
219
251
|
|
|
220
252
|
**Declarations** — `const` and `let` only; Lua's `local` is gone.
|
|
221
253
|
|
|
254
|
+
What a `let` is first given is its type from then on, as in TypeScript:
|
|
255
|
+
`let n = 1` holds numbers, and `n = "a"` is an error. Only a name that starts
|
|
256
|
+
out holding nothing — `let x`, `let x = nil` — takes the type of whatever is
|
|
257
|
+
put in it later. A `let` declared with a type and no value (`let x: number`)
|
|
258
|
+
cannot be read until every way to the read has assigned it — `Variable 'x' is
|
|
259
|
+
used before being assigned` — unless its type admits `nil`. A function
|
|
260
|
+
written in between may read it: it runs later, when it is called.
|
|
261
|
+
|
|
262
|
+
**`n++` and `n--`** are `n += 1` and `n -= 1`: statements, as Luau's compound
|
|
263
|
+
assignments are, so they give no value and cannot be written inside an
|
|
264
|
+
expression. They apply to anything `+=` does — `t.count++`, `xs[i]--` — and
|
|
265
|
+
are checked as it is: the operator has to apply, and what it gives has to fit.
|
|
266
|
+
`--` is also how a comment starts, so `n--` is the decrement only where the
|
|
267
|
+
dashes touch the name (or the `]`) before them and nothing but the end of the
|
|
268
|
+
line, a `;`, a `}` or a comment follows; `n-- note` is still `n` and a
|
|
269
|
+
comment. `++n` is reported, with the spelling tilua uses.
|
|
270
|
+
|
|
222
271
|
**Functions** — `function name() { ... }` declares `name` in the enclosing
|
|
223
272
|
scope; like a TypeScript function declaration it cannot be reassigned.
|
|
224
273
|
`const` and `let` do not apply to functions. `function T.name()` and
|
|
@@ -260,16 +309,40 @@ brings in names that are types and nothing else: unlike TypeScript, using one
|
|
|
260
309
|
as a value is an error, and only type positions — `typeof A` included — may
|
|
261
310
|
name it. Compiled code keeps no trace of it.
|
|
262
311
|
|
|
312
|
+
`import("./m")` is an expression: the module's exports as one value — what
|
|
313
|
+
`import * as M from "./m"` names `M` — read where the expression runs rather
|
|
314
|
+
than before the file does. The module is loaded the first time anything asks
|
|
315
|
+
for it, and the value comes back at once: there is no promise to wait on. The
|
|
316
|
+
path has to be a string literal, since which module it is must be known when
|
|
317
|
+
the project is built. On a line of its own, `import("./setup")` loads the
|
|
318
|
+
module for what it does. `importExpressions(program)` lists every one in a
|
|
319
|
+
file.
|
|
320
|
+
|
|
263
321
|
**Methods of strings, arrays and tables** — a value answers to the methods
|
|
264
|
-
its metatable's `__index` has,
|
|
322
|
+
its metatable's `__index` has. They are members, as a JavaScript object's
|
|
323
|
+
are: read with `.`, a method is a function whose first parameter is its
|
|
324
|
+
`self`, handed to it as any argument is. `:` hands it over for you, as Lua's
|
|
325
|
+
always has:
|
|
265
326
|
|
|
266
327
|
```luau
|
|
267
|
-
const long = names:filter(n =>
|
|
268
|
-
print(names
|
|
328
|
+
const long = names:filter(n => n.length > 3):map(n => n:upper())
|
|
329
|
+
print(names.join(names, ", "), text.trim(text), "abc":upper())
|
|
269
330
|
const point = { x: 1, y: "up" }
|
|
270
331
|
const [x, y] = point:values() -- [number, string]
|
|
332
|
+
const join = names.join -- (self: string[], separator?: string) => string
|
|
271
333
|
```
|
|
272
334
|
|
|
335
|
+
`modes.includes(s)` leaves the `self` out, and is reported as that: call it
|
|
336
|
+
with `:`. The other way round, `o:f(x)` on a function that takes no receiver
|
|
337
|
+
would hand it `o` as `x`, and is reported too. `Stack.push(s, 1)`, which
|
|
338
|
+
passes the receiver by hand, is an ordinary call.
|
|
339
|
+
|
|
340
|
+
**`length`** — `xs.length` is how many a list holds and `text.length` a
|
|
341
|
+
string's length: Luau's `#`, which tilua does not have (`#xs` is an error that
|
|
342
|
+
says to write `xs.length`). A tuple's is known: `[number, string]` has length
|
|
343
|
+
`2`. A table with a `length` of its own is that member, and a class that
|
|
344
|
+
declares `__len` answers what `__len` does.
|
|
345
|
+
|
|
273
346
|
Strings, arrays and tables are the language's own, so their metatables are
|
|
274
347
|
too: the prelude declares them, in tilua, and they are there with no type
|
|
275
348
|
library at all. A string's is Lua's (`upper`, `sub`, with one value where Lua
|
|
@@ -277,7 +350,12 @@ answers several: `find` is an array) plus the JavaScript-shaped ones (`trim`,
|
|
|
277
350
|
`startsWith`, ...); an array's is JavaScript's (`filter`, `map`, `join`, ...),
|
|
278
351
|
with Lua's indices — the first element is 1, `indexOf` answers `nil` rather
|
|
279
352
|
than -1 — and `push`, `pop`, `shift`, `unshift`, `sort` and `reverse` change
|
|
280
|
-
the array.
|
|
353
|
+
the array. Looking for a value is not adding one, so `includes` and `indexOf`
|
|
354
|
+
take any value, not only one the array's type already holds — and `includes`
|
|
355
|
+
is a guard: `if (modes:includes(s))` narrows `s` to a mode. `concat` may join
|
|
356
|
+
arrays of other types (`(T | U)[]`). The ones that only read take a read-only
|
|
357
|
+
list too (`readonly T[]`, what `as const` makes), as TypeScript's
|
|
358
|
+
`ReadonlyArray` does; the ones that change it do not. A table's are `keys`, `values` and `entries`, which answer a tuple
|
|
281
359
|
in the order the table's type wrote its members, not `string[]` as
|
|
282
360
|
JavaScript's `Object.keys` does; the analyzer supplies that order as
|
|
283
361
|
`ObjectKeys<T>`, `ObjectValues<T>` and `ObjectEntries<T>`. A member of a
|
|
@@ -285,7 +363,7 @@ value's own with the same name is what a call reaches, and a class instance,
|
|
|
285
363
|
which has a metatable of its own, has none of them.
|
|
286
364
|
|
|
287
365
|
No Lua array or table really has these methods, so the compiler lowers each
|
|
288
|
-
call into its own runtime (`names
|
|
366
|
+
call into its own runtime (`names.filter(f)` becomes `array.filter(names, f)`);
|
|
289
367
|
a string's Lua methods stay plain method calls.
|
|
290
368
|
|
|
291
369
|
A library adds to a metatable by declaring the same target again, and a
|
|
@@ -340,8 +418,9 @@ such as `print(x)`, and `globalValue` for a global read as a value, such as
|
|
|
340
418
|
`local p = print`. A local with the same name belongs to the author, and no
|
|
341
419
|
hook is asked about it. A runtime can read `__LINES__`, the bundle's line map
|
|
342
420
|
(`nil` outside a bundle), to turn a line Luau reports back into a place in the
|
|
343
|
-
project.
|
|
344
|
-
|
|
421
|
+
project. One that answers with a new array wraps it in `__ARRAY__(...)`, which
|
|
422
|
+
writes it down as an array for `typeof`. The compiler's own lowering of the
|
|
423
|
+
standard library — `console:log`, `error`, `pcall` — is built on these hooks.
|
|
345
424
|
|
|
346
425
|
**A `(` that starts a line** continues the statement above it, as in Lua and
|
|
347
426
|
in JavaScript — `const v = map[key]` followed by `("A"):upper()` is one
|
|
@@ -350,6 +429,53 @@ write `;` before the `(` when a new statement was meant.
|
|
|
350
429
|
|
|
351
430
|
**Object types** — `{ name: T, name?: T, [K]: V }`, and a name that is not an
|
|
352
431
|
identifier is quoted, as in the literal: `{ "Respawn After Kill": boolean }`.
|
|
432
|
+
An index signature may name its key as TypeScript does, `[key: string]: V`, and
|
|
433
|
+
its key may be a template literal: `` { [`on${string}`]: Handler } `` takes every
|
|
434
|
+
`onX`, and an object literal with any other key is excess. A string or a list
|
|
435
|
+
fits a shape that names what it answers to: `"abc"` is a `{ length: number }`.
|
|
436
|
+
Members are separated by `,`, `;` or a line break, and may be written the
|
|
437
|
+
other ways TypeScript writes them:
|
|
438
|
+
|
|
439
|
+
```luau
|
|
440
|
+
type Counter = {
|
|
441
|
+
readonly id: number
|
|
442
|
+
step(by: number): this -- a method: (self: this, by: number) => this
|
|
443
|
+
reset?(): nil -- an optional one
|
|
444
|
+
get total(): number -- read-only, unless a `set` is written too
|
|
445
|
+
set label(value: string)
|
|
446
|
+
(n: number): string -- calling the value; several are overloads
|
|
447
|
+
new (seed: number): Counter -- what its `new` makes: { new: (seed: number) => Counter }
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
A method is called with `:`, as a class's methods are: its first parameter is
|
|
452
|
+
the receiver, typed `this`, so `counter:step(1)` hands `counter` over and
|
|
453
|
+
`counter.step(1)` is the missing argument it is. A signature with no return
|
|
454
|
+
type returns `nil`. Calling the value and having members too is the functions
|
|
455
|
+
and the members together — Lua builds such a value with a `__call` metatable.
|
|
456
|
+
|
|
457
|
+
**Interfaces** — `interface Name<T> extends A, B<T> { ... }`, with the members
|
|
458
|
+
an object type takes. It is a type alias for the bases and the members
|
|
459
|
+
together, with what TypeScript adds: two interfaces of one name are one, their
|
|
460
|
+
members merged (a method written in each is an overload set), and a member
|
|
461
|
+
that does not fit the one it replaces in a base is reported. `export
|
|
462
|
+
interface` exports it, as `export type` does.
|
|
463
|
+
|
|
464
|
+
```luau
|
|
465
|
+
interface Named { name: string }
|
|
466
|
+
interface Greeter extends Named {
|
|
467
|
+
greet(other: Named): string
|
|
468
|
+
}
|
|
469
|
+
class Person implements Greeter {
|
|
470
|
+
name = "Ada"
|
|
471
|
+
function greet(other: Named): string { return `hi ${other.name}` }
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**`this` in a type** — TypeScript's polymorphic `this`: the value a member is
|
|
476
|
+
read from. A method that returns `this` returns whatever it was called on, so
|
|
477
|
+
`Builder.new():add(1):done()` keeps the subclass's members through the chain.
|
|
478
|
+
Inside the class it is the instance.
|
|
353
479
|
|
|
354
480
|
**Optionality** — there is no `T?` shorthand. `?` in type position always
|
|
355
481
|
belongs to a conditional type, and in expression position to a ternary or an
|
|
@@ -372,6 +498,29 @@ ternary. Parentheses end a chain. A chain cannot be assigned to (`a?.b = 1` is
|
|
|
372
498
|
an error). A chain that got through narrows what it tested: inside
|
|
373
499
|
`if (part?.Parent)`, and `if (part?.Name == "Door")`, `part` is not nil.
|
|
374
500
|
|
|
501
|
+
**`??`** — `a ?? b` is `a`, or `b` where `a` is nil; `b` is read only then.
|
|
502
|
+
Unlike `a or b` a `false` stays, which is what a `boolean | nil` wants:
|
|
503
|
+
`settings.enabled ?? true`. As in TypeScript it does not mix with `and` or
|
|
504
|
+
`or` without parentheses.
|
|
505
|
+
|
|
506
|
+
**`in`** — `"key" in t` asks whether `t` holds something at `key`: in Lua a
|
|
507
|
+
key whose value is nil is a key that is not there, so it is `t.key ~= nil`.
|
|
508
|
+
The key is a string or a number and `t` a table. It narrows as TypeScript's
|
|
509
|
+
does: where it holds, `t` is one of the union members that can have `key`, and
|
|
510
|
+
`t.key` is not nil; where it does not, `t` is none of the members that must.
|
|
511
|
+
|
|
512
|
+
**`instanceof`** — `v instanceof Dog` is true when `v` was built by `Dog` or
|
|
513
|
+
by a class extending it. It walks the links every instance has: `v.ClassObject`,
|
|
514
|
+
then each `.ParentClass` up the chain. It narrows `v` to `Dog`, and only a
|
|
515
|
+
class written in tilua can stand on its right — a `declare class` is the
|
|
516
|
+
host's, and Roblox's answer `IsA` instead.
|
|
517
|
+
|
|
518
|
+
**Accessors in an object literal** — `{ get full() { ... }, set full(v) { ...
|
|
519
|
+
} }` reads and writes `full` through functions, with the object as `this`. Lua
|
|
520
|
+
has no such thing, so the object gets a metatable that runs them; a name with a
|
|
521
|
+
getter and no setter is read-only, and assigning it is an error at runtime as
|
|
522
|
+
well as in the types.
|
|
523
|
+
|
|
375
524
|
**Classes** — types are structural, except for classes. A definitions file
|
|
376
525
|
declares one with `declare class`, and it is nominal, as Roblox's classes are:
|
|
377
526
|
|
|
@@ -383,8 +532,8 @@ declare class Part extends BasePart { Shape: EnumItem }
|
|
|
383
532
|
A `Part` is a `BasePart` and an `Instance` because it extends them. A
|
|
384
533
|
`ReplicatedStorage` is not a `Part`, and no table literal is an `Instance`,
|
|
385
534
|
however alike their members. A class still fits a shape that names members it
|
|
386
|
-
has (`{ Name: string }`). It is not a table, though, so `typeof
|
|
387
|
-
|
|
535
|
+
has (`{ Name: string }`). It is not a table, though, so `typeof part` is
|
|
536
|
+
`"Instance"`, as the library's `TypeofNames` says, not `"object"`. Members are inherited, and a subclass
|
|
388
537
|
may narrow one (`Parent: SomeFolder`).
|
|
389
538
|
|
|
390
539
|
**`class ... { }`** — a class written in code, rather than declared in a
|
|
@@ -504,7 +653,7 @@ class Square extends Shape {
|
|
|
504
653
|
instances' metatable, so `function __add(other: Vec): Vec` in a class is the
|
|
505
654
|
`+` of its instances — and the type checker reads it the same way: `a + b`
|
|
506
655
|
between two `Vec`s is a `Vec`. `__sub`, `__mul`, `__div`, `__idiv`, `__mod`,
|
|
507
|
-
`__pow`, `__unm`, `__concat`, `__len` (
|
|
656
|
+
`__pow`, `__unm`, `__concat`, `__len` (`v.length`), `__eq`, `__lt` and `__le` (`<`,
|
|
508
657
|
`>`, `<=`, `>=`), `__call` (calling an instance), `__iter` (`for a, b in v`)
|
|
509
658
|
and `__tostring` all work this way, and a class extending another inherits
|
|
510
659
|
them. Each is checked: it takes the operands Luau hands it, returns what Luau
|
|
@@ -690,14 +839,24 @@ destructuring instead.
|
|
|
690
839
|
```luau
|
|
691
840
|
join("-", ...names) -- every name
|
|
692
841
|
join("-", ...names, "z") -- and one more after them
|
|
693
|
-
add3(...
|
|
842
|
+
add3(...trio) -- `trio: [number, number, number]`: exactly three
|
|
694
843
|
const both = [...names, "z"]
|
|
695
844
|
```
|
|
696
845
|
|
|
697
|
-
What the array holds is checked against what it fills. How many
|
|
698
|
-
not known, so
|
|
699
|
-
|
|
700
|
-
|
|
846
|
+
What the array holds is checked against what it fills. How many an array
|
|
847
|
+
holds is not known, so, as in TypeScript, it can only fill a rest parameter —
|
|
848
|
+
`add3(...nums)` with `nums: number[]` is an error, and so is `h(...nums)` for
|
|
849
|
+
an `h(a: number, ...more: number[])`, whose `a` it would have to fill. A
|
|
850
|
+
tuple holds a known value at each position: it fills exactly that many
|
|
851
|
+
parameters, checked one by one and counted, several tuples together too.
|
|
852
|
+
Spreading something that is not a list is reported, and so is spreading a
|
|
853
|
+
list that may be nil — write `...(xs ?? [])`.
|
|
854
|
+
|
|
855
|
+
In an object literal, `...source` copies what `source` has: over a union, one
|
|
856
|
+
object per member; over a value that may be nil, every member optional; over
|
|
857
|
+
a class instance, its fields — its methods live on the class table, which a
|
|
858
|
+
copy does not reach. A copy is a new table, so nothing in it is read-only.
|
|
859
|
+
`const [first, ...rest] = tuple` leaves `rest` a tuple of what is left.
|
|
701
860
|
|
|
702
861
|
**Branded types** — nothing in the type model is about branding; an
|
|
703
862
|
intersection already means it. `string & { __brand }` is assignable to
|
|
@@ -714,7 +873,7 @@ findUser(id) -- ok
|
|
|
714
873
|
findUser("raw") -- '"raw"' is not assignable to 'UserId'
|
|
715
874
|
findUser(postId) -- 'PostId' is not assignable to 'UserId'
|
|
716
875
|
|
|
717
|
-
|
|
876
|
+
id.length -- still a string: 5
|
|
718
877
|
id:upper() -- and its methods, giving a plain string
|
|
719
878
|
```
|
|
720
879
|
|
|
@@ -753,6 +912,26 @@ parameter reads `Extract<Rows, { Page: P }>["Skills"][number]`. A type that
|
|
|
753
912
|
waits on a type parameter — a conditional, an index, `T[K]` — is worked out
|
|
754
913
|
where that parameter is.
|
|
755
914
|
|
|
915
|
+
**Functions compare as under `strictFunctionTypes`.** A function fits a
|
|
916
|
+
function type when it takes whatever that type's callers pass — its parameters
|
|
917
|
+
are contravariant — and needs no more arguments than they hand it: `(x: Part)
|
|
918
|
+
=> nil` is not a `(x: Instance) => nil`, which may be called with any
|
|
919
|
+
instance. A method — a function taking its receiver first, `self` or `this`
|
|
920
|
+
— is compared both ways, as TypeScript compares methods, so an override may
|
|
921
|
+
narrow what it takes.
|
|
922
|
+
|
|
923
|
+
**Every way through returns.** A function that declares a return type which
|
|
924
|
+
does not admit `nil` has to return on every way to its end: `Not every path
|
|
925
|
+
returns a value` otherwise. A call that is `never` (`error(...)`) ends a way,
|
|
926
|
+
and so does `while (true)` with no `break` of its own.
|
|
927
|
+
|
|
928
|
+
**An `if` chain can be exhaustive.** tilua has no `switch`; an `if` /
|
|
929
|
+
`elseif` chain whose tests leave no value for an `else` — every member of
|
|
930
|
+
`x: "a" | "b"` tested — has no way past it but its branches. A function whose
|
|
931
|
+
chain returns in every branch needs no `return` after it, and after it `x` is
|
|
932
|
+
`never`, which is how `const unreachable: never = x` checks that a new
|
|
933
|
+
member was not forgotten.
|
|
934
|
+
|
|
756
935
|
**Trailing commas** are allowed wherever TypeScript allows them: parameter
|
|
757
936
|
lists, call arguments, generic parameters and type arguments, tables, arrays,
|
|
758
937
|
tuples, imports and exports.
|
|
@@ -789,25 +968,95 @@ type says so — it is `any`, and `any` turns off every check made of it.
|
|
|
789
968
|
**Types** — unions, intersections, tuples `[A, B]` (with a rest, `[A,
|
|
790
969
|
...B[]]`), `keyof`, `T[K]`, conditional types with
|
|
791
970
|
`infer`, mapped types with `as` remapping, template literal types
|
|
792
|
-
(`` `on${Event}` ``), and
|
|
971
|
+
(`` `on${Event}` ``), and `Exclude<A, B>`. The utility types
|
|
793
972
|
(`Partial`, `Pick`, `Omit`, `ReturnType`, `Parameters`, `Exclude`, …) are
|
|
794
973
|
built in, and written in tilua on top of those rather than special-cased in
|
|
795
974
|
the analyzer. A type library or a file may declare one again; the later
|
|
796
975
|
declaration wins.
|
|
797
976
|
|
|
977
|
+
Type-level programs work as they do in TypeScript. A tuple may spread anywhere
|
|
978
|
+
(`[...A, ...B]`, `[infer H, ...infer R]`) and name its elements
|
|
979
|
+
(`[x: number, y?: number]`) — an element marked `?` may be left out, so
|
|
980
|
+
`[number, string?]` takes `[1]` and its `length` is `1 | 2`, and
|
|
981
|
+
`Parameters<(a: number, b?: string) => nil>` is `[a: number, b?: string]`; a
|
|
982
|
+
list of any length may stand before the elements written after it
|
|
983
|
+
(`[...unknown[], infer L]`); `T["length"]` counts a tuple, and `keyof` a list is
|
|
984
|
+
its positions and its `length`, `number | "length"` (`keyof string[]` is the
|
|
985
|
+
list's keys, `(keyof T)[]` a list of keys, as TypeScript reads them); an alias may
|
|
986
|
+
recurse (`type Reverse<T> = T extends [infer H, ...infer R] ? [...Reverse<R>,
|
|
987
|
+
H] : []`); a template literal pattern takes a string apart (`` S extends
|
|
988
|
+
`${infer A}.${infer B}` ``), and `infer N extends number` reads `"42"` as
|
|
989
|
+
`42`. `object` is anything that is not a primitive. One difference: a tuple's
|
|
990
|
+
positions count from 1, as Lua's do, so the first element of `T` is `T[1]`.
|
|
991
|
+
A recursion that never ends stops after 100 levels and gives `any`.
|
|
992
|
+
|
|
993
|
+
**What `${...}` may hold** — a template string writes each value with
|
|
994
|
+
`tostring`, so it takes what reads as text: a string, a number, a boolean,
|
|
995
|
+
`nil`, and a value that says how to print itself — a class with `__tostring`,
|
|
996
|
+
or one the host made (a Roblox `Instance`, a `Vector3`). A table, a list, a
|
|
997
|
+
function or a class instance without `__tostring` would print as its address
|
|
998
|
+
(`table: 0x...`), and is an error. A template literal *type* holds a string,
|
|
999
|
+
a number, a boolean or `nil` in each placeholder, as TypeScript's does:
|
|
1000
|
+
`` `a${number[]}` `` is an error, and so is a `T extends object` there.
|
|
1001
|
+
|
|
1002
|
+
A generic function may take and give variadic tuples: `<T extends unknown[]>(xs:
|
|
1003
|
+
[...T]): T` reads `[1, "a"]` as `[number, string]`, `(...args: [...T, string])`
|
|
1004
|
+
takes the last argument apart from the others, and `(fn: (...args: [...A,
|
|
1005
|
+
...B]) => R, ...a: A): (...b: B) => R` binds the first arguments of `fn`.
|
|
1006
|
+
|
|
1007
|
+
`Exclude<T, U>` — what of `T` is not a `U` — is the analyzer's own, and unlike
|
|
1008
|
+
TypeScript's it is kept where TypeScript's would forget it: `Exclude<unknown,
|
|
1009
|
+
nil>` and `Exclude<any, nil>` stay, as a value known not to be `nil`, and so
|
|
1010
|
+
does `Exclude<string, "a">`, a string that is not `"a"` — of which `if (s ==
|
|
1011
|
+
"a")` is reported as a test that can never pass. Over a union it drops the
|
|
1012
|
+
members it takes whole (`Exclude<"a" | "b", "b">` is `"a"`), and `boolean` is
|
|
1013
|
+
the union `true | false`.
|
|
1014
|
+
tilua has no `A - B` in types; `Exclude` is the one way to write it.
|
|
1015
|
+
|
|
798
1016
|
A call may write its type arguments out — `find<Folder>("Remotes")`,
|
|
799
1017
|
`inst:WaitForChild<Folder>("Remotes")` — and a type parameter may have a
|
|
800
1018
|
default (`<T = Instance>`) for the calls that do not. `a < b > (c)` is still
|
|
801
1019
|
three operators: only a call after the `>` makes it type arguments.
|
|
802
1020
|
|
|
803
|
-
`<const T>` infers an argument at its narrowest, as in TypeScript 5.
|
|
1021
|
+
`<const T>` infers an argument at its narrowest, as in TypeScript 5. Without it
|
|
1022
|
+
a literal is still kept where the function gives `T` itself back —
|
|
1023
|
+
`identity("x")` is `"x"`, and a `let` it is given to widens it to `string` —
|
|
1024
|
+
and a `K extends keyof T`, or a `T extends string`, keeps the key or the text
|
|
1025
|
+
written. Literals of one primitive read for one parameter are either:
|
|
1026
|
+
`pair(1, 2)` is `1 | 2`. `as const` makes its lists read-only:
|
|
1027
|
+
`[1, 2] as const` is a `readonly [1, 2]`. `ReturnType` of an overloaded
|
|
1028
|
+
function is its last signature's, as in TypeScript.
|
|
1029
|
+
|
|
1030
|
+
`NoInfer<T>` is `T`, except that a call does not work `T` out from an
|
|
1031
|
+
argument written there: `function pick<T>(a: T, fallback: NoInfer<T>): T`
|
|
1032
|
+
takes `T` from `a` alone, and checks `fallback` against it.
|
|
1033
|
+
|
|
1034
|
+
A type alias or an interface may say how it compares by a parameter:
|
|
1035
|
+
`type Getter<out T>` as `T` does, `type Setter<in T>` the other way round,
|
|
1036
|
+
`<in out T>` only by the same `T`. It is checked against the body —
|
|
1037
|
+
`type Getter<in T> = { get: () => T }` is an error — and a function's or a
|
|
1038
|
+
class's parameters take none.
|
|
1039
|
+
|
|
1040
|
+
`new (name: string) => Dog` is the type of a class table that builds a `Dog`:
|
|
1041
|
+
tilua builds with `Dog.new(name)`, so it is `{ new: (name: string) => Dog }`.
|
|
1042
|
+
`InstanceType<typeof Dog>` and `ConstructorParameters<typeof Dog>` read it
|
|
1043
|
+
back, as TypeScript's do.
|
|
804
1044
|
|
|
805
1045
|
`typeof x` in a type is TypeScript's type query — the type of a value
|
|
806
1046
|
(`typeof config`, `typeof config.volume`, `ReturnType<typeof f>`). Luau's
|
|
807
|
-
`typeof(expr)` spelling
|
|
808
|
-
`typeof
|
|
1047
|
+
`typeof(expr)` spelling is an error here as well. It is compile-time only, unrelated to the
|
|
1048
|
+
`typeof v` operator in an expression, which answers a string as the program
|
|
1049
|
+
runs.
|
|
809
1050
|
|
|
810
1051
|
**Modules** — `import` / `export`, export lists, re-exports and `export *`.
|
|
1052
|
+
`export type { A }` (and `export type { A } from "./m"`) exports types and
|
|
1053
|
+
nothing else; naming a value there is an error.
|
|
1054
|
+
|
|
1055
|
+
**What TypeScript has and tilua does not** says what to write instead, rather
|
|
1056
|
+
than reading as something else: `enum`, `namespace`, `try` / `catch`,
|
|
1057
|
+
`switch`, `declare module` and `declare global` are each reported with the
|
|
1058
|
+
tilua spelling. (A name followed by `{` is otherwise a call with a table
|
|
1059
|
+
argument, so `enum Color { ... }` would have meant `enum(Color { ... })`.)
|
|
811
1060
|
|
|
812
1061
|
**`satisfies`** — checks a value against a type without giving it that type,
|
|
813
1062
|
as in TypeScript 4.9:
|
|
@@ -869,11 +1118,9 @@ analyzeTypes(program, scopes, {
|
|
|
869
1118
|
|
|
870
1119
|
## Known limitations
|
|
871
1120
|
|
|
872
|
-
- `export * as ns from`
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
- Accessing a property a type does not have yields `unknown` rather than an
|
|
876
|
-
error; assigning to a `readonly` property is not reported.
|
|
1121
|
+
- `export * as ns from` is not supported.
|
|
1122
|
+
- `setmetatable` is not modelled: a table given a metatable at runtime keeps
|
|
1123
|
+
the type it had.
|
|
877
1124
|
- A sourcemap child whose name is not an identifier (`"My Part"`) is not typed.
|
|
878
1125
|
|
|
879
1126
|
## Development
|