# TypeScriptToLua compiles its own Lua library and pins one exact TypeScript version

> A transpiler sold as a way to keep large Lua projects maintainable, whose npm package pins the compiler to a single TypeScript version, builds its Lua library twice from two tsconfigs, and cuts releases through npm rather than through GitHub.

**TypeScriptToLua/TypeScriptToLua** — Typescript to lua transpiler. https://typescripttolua.github.io/

- Repository: https://github.com/TypeScriptToLua/TypeScriptToLua
- Stars: 2,550 · Forks: 187
- Language: TypeScript
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/typescripttolua-typescripttolua

## tstl builds its own Lua library, twice, from two tsconfigs

The build script is where the project eats its own cooking. `npm run build` runs the TypeScript compiler and then `build-lualib`, and that second step does not use `tsc` at all. It runs the freshly compiled `dist/tstl.js` against `src/lualib/tsconfig.json`, then runs it again against `src/lualib/tsconfig.lua50.json`. So the Lua part of the toolchain is produced by the transpiler that was just built, in two passes with two different configurations, and the second file name says which Lua generation it targets. The published package carries those results outward: its files list includes `dist/**/*.lua` and `dist/lualib/**/*.json` next to the JavaScript, which means an installed `typescript-to-lua` brings Lua sources and a Lua library description with it rather than shipping a JavaScript only artifact.

## The peer dependency is one exact compiler version, not a range

Most transpilers accept a range of host compiler versions. This one does not. The package manifest lists TypeScript as a peer dependency at exactly 6.0.2, with no caret and no range, alongside a Node engine floor of `>=16.10.0`. That pairing is the whole compatibility story: the transpiler is welded to a single release of the compiler it sits on top of, and the two are versioned independently, so a project that upgrades its TypeScript for any other reason has to move the transpiler at the same time or accept an unsupported pairing. The runtime dependencies are stricter still in the opposite direction, with exact pins such as `enhanced-resolve` at 5.8.2 and the language extensions package at 1.19.0, while `picomatch` is the one entry written as a range.

## Releases are cut with npm version, which is why the releases list is empty

The repository has no GitHub releases. What it has instead is a version field in `package.json`, currently 1.37.1, and a CHANGELOG.md at the root that the README links as Changelog. The scripts explain the gap. `preversion` runs the build and the tests, and `postversion` runs `git push && git push --tags`, so publishing a version pushes straight to the repository and pushes the tag with it, and nothing in that chain creates a GitHub release with notes attached. A reader who wants to know what changed therefore reads a markdown file, and a reader who wants to pin a version has to take the npm version number and trust that the tag matches it.

## The README is short, and every hard question goes to a website

The top level document is about a hundred and sixty words with four links: documentation, a Try Online playground, the changelog, and the contribution guidelines, plus a Discord invite and a coverage badge. Everything a reader actually needs sits behind the documentation link. What the README itself does not mention is the interesting part: which Lua versions are targeted beyond the one implied by the `tsconfig.lua50.json` filename, what the transpiler does with TypeScript constructs that have no Lua equivalent, how type information is turned into Lua, or where the known gaps are recorded. The repository records no homepage of its own, while the package manifest points at the documentation site, and the default branch is `master` rather than the more usual `main`.

## An existing Lua API has to be declared before your code can call it

The premise is a migration argument. The README's case is that large Lua projects get hard to maintain and easy to get wrong, and that writing the same code in TypeScript buys back readability plus editor tooling, with what it calls the option of declaring any existing API using TypeScript declaration files. Read that clause as the cost centre of the project. A Lua module you want to call does not become callable because the transpiler understands it; it becomes callable because somebody writes a `.d.ts` for it. For a project built on a large third party Lua library, that declaration work is the bulk of the effort, and the README offers no guidance on partial declarations, on what a missing one does at compile time, or on how to keep declarations in step with the Lua they describe. The two commands the README does give are the entire entry path, adding the package as a development dependency and then running the bundled application.

```bash
$ npm install -D typescript-to-lua
```

```
$ npx tstl
```

## The lint and editor files here belong to the transpiler, not to your project

The pitch lists ESLint, Prettier, Visual Studio Code, and WebStorm as tooling support, and the repository root is full of the matching files: `eslint.config.mjs`, `.prettierrc.json`, `.prettierignore`, `.editorconfig`, `.vscode/`, and `.devcontainer/`. Those configure the development of the transpiler itself. The consumer side is a documentation page about editor support, not a shared config, so a project adopting the output gets none of these files unless it copies them. The same split shows up in testing: `jest.config.js` and `.codecov.yml` sit next to the source for the transpiler's own suite, and a `benchmark/` directory is present at the root with no note anywhere in the README about what is measured, on what input, or against which Lua implementation.

## The test gate is lint, a strict type check, and jest

Running the tests pulls in three other gates first. The `pretest` script chains lint, then `check:language-extensions`, then `build-lualib`, so the Lua library is rebuilt before jest runs rather than being restored from a previous build. Lint itself is two tools, `eslint .` and a `prettier --check` that prints a fix hint when it fails. The type check is the narrow one: `tsc --strict --ignoreConfig language-extensions/index.d.ts`, which type checks a single declaration file under strict mode with the project config deliberately ignored. That file is the public surface for the `@typescript-to-lua/language-extensions` package, pinned at 1.19.0, so the extension declarations consumers compile against are validated on their own, separately from the compiler that will consume them.

## Conclusion

Judge this project as a migration tool rather than a drop in replacement, because the pitch depends on you declaring existing Lua APIs yourself in TypeScript declaration files and the README says nothing about how unsupported syntax or a missing declaration is handled. Before adopting it, check two things in the package manifest: the peer dependency is pinned to one exact TypeScript version, so a compiler upgrade is your problem, and the engine floor is node 16.10.0. If you need a versioned artifact to pin, build one yourself, since the repository publishes no releases and keeps its history in CHANGELOG.md.

## FAQ

### How do I install and run TypeScriptToLua?

Add the npm package as a development dependency with `npm install -D typescript-to-lua`, then run the bundled command line application with `npx tstl`. The README says it is used similarly to `tsc`, and the package needs Node 16.10.0 or newer.

### Which TypeScript version does TypeScriptToLua work with?

The package manifest lists typescript 6.0.2 as a peer dependency at that exact version, with no range, and declares a node engine floor of 16.10.0. The transpiler package itself is at version 1.37.1.

### Does TypeScriptToLua publish GitHub releases?

No. The repository has no GitHub releases. The version lives in package.json at 1.37.1 and the history is kept in CHANGELOG.md, with the postversion script pushing to git and pushing tags after each version bump.

### Can I try TypeScriptToLua without installing it?

Yes. The README links a Try Online playground at typescripttolua.github.io/play/, and the full documentation sits at typescripttolua.github.io.

### What does TypeScriptToLua need before it can use an existing Lua API?

The README's stated approach is declaring any existing API with TypeScript declaration files. A Lua module becomes usable once somebody writes a declaration file for it, which makes declaration writing rather than transpiling the main part of the work.

## Sources

- [Issues](https://github.com/TypeScriptToLua/TypeScriptToLua/issues)
- [License: MIT](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/LICENSE)
- [README](https://github.com/TypeScriptToLua/TypeScriptToLua/blob/master/README.md)
- [TypeScriptToLua/TypeScriptToLua on GitHub](https://github.com/TypeScriptToLua/TypeScriptToLua)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/typescripttolua-typescripttolua
