@tilua/parser 4.0.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 +258 -30
- package/dist/index.cjs +3001 -483
- package/dist/index.d.cts +217 -39
- package/dist/index.d.ts +217 -39
- package/dist/index.js +2983 -482
- 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,6 +132,7 @@ 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
|
|
@@ -236,6 +251,23 @@ is what a JavaScript arrow is for.
|
|
|
236
251
|
|
|
237
252
|
**Declarations** — `const` and `let` only; Lua's `local` is gone.
|
|
238
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
|
+
|
|
239
271
|
**Functions** — `function name() { ... }` declares `name` in the enclosing
|
|
240
272
|
scope; like a TypeScript function declaration it cannot be reassigned.
|
|
241
273
|
`const` and `let` do not apply to functions. `function T.name()` and
|
|
@@ -277,16 +309,40 @@ brings in names that are types and nothing else: unlike TypeScript, using one
|
|
|
277
309
|
as a value is an error, and only type positions — `typeof A` included — may
|
|
278
310
|
name it. Compiled code keeps no trace of it.
|
|
279
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
|
+
|
|
280
321
|
**Methods of strings, arrays and tables** — a value answers to the methods
|
|
281
|
-
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:
|
|
282
326
|
|
|
283
327
|
```luau
|
|
284
|
-
const long = names:filter(n =>
|
|
285
|
-
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())
|
|
286
330
|
const point = { x: 1, y: "up" }
|
|
287
331
|
const [x, y] = point:values() -- [number, string]
|
|
332
|
+
const join = names.join -- (self: string[], separator?: string) => string
|
|
288
333
|
```
|
|
289
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
|
+
|
|
290
346
|
Strings, arrays and tables are the language's own, so their metatables are
|
|
291
347
|
too: the prelude declares them, in tilua, and they are there with no type
|
|
292
348
|
library at all. A string's is Lua's (`upper`, `sub`, with one value where Lua
|
|
@@ -294,7 +350,12 @@ answers several: `find` is an array) plus the JavaScript-shaped ones (`trim`,
|
|
|
294
350
|
`startsWith`, ...); an array's is JavaScript's (`filter`, `map`, `join`, ...),
|
|
295
351
|
with Lua's indices — the first element is 1, `indexOf` answers `nil` rather
|
|
296
352
|
than -1 — and `push`, `pop`, `shift`, `unshift`, `sort` and `reverse` change
|
|
297
|
-
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
|
|
298
359
|
in the order the table's type wrote its members, not `string[]` as
|
|
299
360
|
JavaScript's `Object.keys` does; the analyzer supplies that order as
|
|
300
361
|
`ObjectKeys<T>`, `ObjectValues<T>` and `ObjectEntries<T>`. A member of a
|
|
@@ -302,7 +363,7 @@ value's own with the same name is what a call reaches, and a class instance,
|
|
|
302
363
|
which has a metatable of its own, has none of them.
|
|
303
364
|
|
|
304
365
|
No Lua array or table really has these methods, so the compiler lowers each
|
|
305
|
-
call into its own runtime (`names
|
|
366
|
+
call into its own runtime (`names.filter(f)` becomes `array.filter(names, f)`);
|
|
306
367
|
a string's Lua methods stay plain method calls.
|
|
307
368
|
|
|
308
369
|
A library adds to a metatable by declaring the same target again, and a
|
|
@@ -358,8 +419,8 @@ such as `print(x)`, and `globalValue` for a global read as a value, such as
|
|
|
358
419
|
hook is asked about it. A runtime can read `__LINES__`, the bundle's line map
|
|
359
420
|
(`nil` outside a bundle), to turn a line Luau reports back into a place in the
|
|
360
421
|
project. One that answers with a new array wraps it in `__ARRAY__(...)`, which
|
|
361
|
-
writes it down as an array for `typeof`.
|
|
362
|
-
these hooks.
|
|
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.
|
|
363
424
|
|
|
364
425
|
**A `(` that starts a line** continues the statement above it, as in Lua and
|
|
365
426
|
in JavaScript — `const v = map[key]` followed by `("A"):upper()` is one
|
|
@@ -368,6 +429,53 @@ write `;` before the `(` when a new statement was meant.
|
|
|
368
429
|
|
|
369
430
|
**Object types** — `{ name: T, name?: T, [K]: V }`, and a name that is not an
|
|
370
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.
|
|
371
479
|
|
|
372
480
|
**Optionality** — there is no `T?` shorthand. `?` in type position always
|
|
373
481
|
belongs to a conditional type, and in expression position to a ternary or an
|
|
@@ -390,6 +498,29 @@ ternary. Parentheses end a chain. A chain cannot be assigned to (`a?.b = 1` is
|
|
|
390
498
|
an error). A chain that got through narrows what it tested: inside
|
|
391
499
|
`if (part?.Parent)`, and `if (part?.Name == "Door")`, `part` is not nil.
|
|
392
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
|
+
|
|
393
524
|
**Classes** — types are structural, except for classes. A definitions file
|
|
394
525
|
declares one with `declare class`, and it is nominal, as Roblox's classes are:
|
|
395
526
|
|
|
@@ -522,7 +653,7 @@ class Square extends Shape {
|
|
|
522
653
|
instances' metatable, so `function __add(other: Vec): Vec` in a class is the
|
|
523
654
|
`+` of its instances — and the type checker reads it the same way: `a + b`
|
|
524
655
|
between two `Vec`s is a `Vec`. `__sub`, `__mul`, `__div`, `__idiv`, `__mod`,
|
|
525
|
-
`__pow`, `__unm`, `__concat`, `__len` (
|
|
656
|
+
`__pow`, `__unm`, `__concat`, `__len` (`v.length`), `__eq`, `__lt` and `__le` (`<`,
|
|
526
657
|
`>`, `<=`, `>=`), `__call` (calling an instance), `__iter` (`for a, b in v`)
|
|
527
658
|
and `__tostring` all work this way, and a class extending another inherits
|
|
528
659
|
them. Each is checked: it takes the operands Luau hands it, returns what Luau
|
|
@@ -708,14 +839,24 @@ destructuring instead.
|
|
|
708
839
|
```luau
|
|
709
840
|
join("-", ...names) -- every name
|
|
710
841
|
join("-", ...names, "z") -- and one more after them
|
|
711
|
-
add3(...
|
|
842
|
+
add3(...trio) -- `trio: [number, number, number]`: exactly three
|
|
712
843
|
const both = [...names, "z"]
|
|
713
844
|
```
|
|
714
845
|
|
|
715
|
-
What the array holds is checked against what it fills. How many
|
|
716
|
-
not known, so
|
|
717
|
-
|
|
718
|
-
|
|
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.
|
|
719
860
|
|
|
720
861
|
**Branded types** — nothing in the type model is about branding; an
|
|
721
862
|
intersection already means it. `string & { __brand }` is assignable to
|
|
@@ -732,7 +873,7 @@ findUser(id) -- ok
|
|
|
732
873
|
findUser("raw") -- '"raw"' is not assignable to 'UserId'
|
|
733
874
|
findUser(postId) -- 'PostId' is not assignable to 'UserId'
|
|
734
875
|
|
|
735
|
-
|
|
876
|
+
id.length -- still a string: 5
|
|
736
877
|
id:upper() -- and its methods, giving a plain string
|
|
737
878
|
```
|
|
738
879
|
|
|
@@ -771,6 +912,26 @@ parameter reads `Extract<Rows, { Page: P }>["Skills"][number]`. A type that
|
|
|
771
912
|
waits on a type parameter — a conditional, an index, `T[K]` — is worked out
|
|
772
913
|
where that parameter is.
|
|
773
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
|
+
|
|
774
935
|
**Trailing commas** are allowed wherever TypeScript allows them: parameter
|
|
775
936
|
lists, call arguments, generic parameters and type arguments, tables, arrays,
|
|
776
937
|
tuples, imports and exports.
|
|
@@ -807,18 +968,79 @@ type says so — it is `any`, and `any` turns off every check made of it.
|
|
|
807
968
|
**Types** — unions, intersections, tuples `[A, B]` (with a rest, `[A,
|
|
808
969
|
...B[]]`), `keyof`, `T[K]`, conditional types with
|
|
809
970
|
`infer`, mapped types with `as` remapping, template literal types
|
|
810
|
-
(`` `on${Event}` ``), and
|
|
971
|
+
(`` `on${Event}` ``), and `Exclude<A, B>`. The utility types
|
|
811
972
|
(`Partial`, `Pick`, `Omit`, `ReturnType`, `Parameters`, `Exclude`, …) are
|
|
812
973
|
built in, and written in tilua on top of those rather than special-cased in
|
|
813
974
|
the analyzer. A type library or a file may declare one again; the later
|
|
814
975
|
declaration wins.
|
|
815
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
|
+
|
|
816
1016
|
A call may write its type arguments out — `find<Folder>("Remotes")`,
|
|
817
1017
|
`inst:WaitForChild<Folder>("Remotes")` — and a type parameter may have a
|
|
818
1018
|
default (`<T = Instance>`) for the calls that do not. `a < b > (c)` is still
|
|
819
1019
|
three operators: only a call after the `>` makes it type arguments.
|
|
820
1020
|
|
|
821
|
-
`<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.
|
|
822
1044
|
|
|
823
1045
|
`typeof x` in a type is TypeScript's type query — the type of a value
|
|
824
1046
|
(`typeof config`, `typeof config.volume`, `ReturnType<typeof f>`). Luau's
|
|
@@ -827,6 +1049,14 @@ three operators: only a call after the `>` makes it type arguments.
|
|
|
827
1049
|
runs.
|
|
828
1050
|
|
|
829
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 { ... })`.)
|
|
830
1060
|
|
|
831
1061
|
**`satisfies`** — checks a value against a type without giving it that type,
|
|
832
1062
|
as in TypeScript 4.9:
|
|
@@ -888,11 +1118,9 @@ analyzeTypes(program, scopes, {
|
|
|
888
1118
|
|
|
889
1119
|
## Known limitations
|
|
890
1120
|
|
|
891
|
-
- `export * as ns from`
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
- Accessing a property a type does not have yields `unknown` rather than an
|
|
895
|
-
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.
|
|
896
1124
|
- A sourcemap child whose name is not an identifier (`"My Part"`) is not typed.
|
|
897
1125
|
|
|
898
1126
|
## Development
|