Coder Social home page Coder Social logo

typescripttolua / typescripttolua Goto Github PK

View Code? Open in Web Editor NEW
2.0K 31.0 165.0 6.33 MB

Typescript to lua transpiler. https://typescripttolua.github.io/

License: MIT License

TypeScript 97.53% Lua 1.13% JavaScript 1.33%
typescript lua transpiler compiler converter lua-transpiler

typescripttolua's Introduction


A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua!

Large projects written in Lua can become hard to maintain and make it easy to make mistakes. Writing code in TypeScript instead improves maintainability, readability and robustness, with the added bonus of good tooling support (including ESLint, Prettier, Visual Studio Code and WebStorm). This project is useful in any environment where Lua code is accepted, with the powerful option of simply declaring any existing API using TypeScript declaration files.

Getting Started

To install TypeScriptToLua add the typescript-to-lua npm package:

$ npm install -D typescript-to-lua

This package includes the tstl command line application, which can be used similarly to tsc:

$ npx tstl

For more information, check out Getting Started in our documentation.

typescripttolua's People

Contributors

aabajyan avatar ark120202 avatar chrisd08 avatar dependabot[bot] avatar dmarcuse avatar doctorgester avatar endel avatar gakada avatar glassbricks avatar hazzard993 avatar imxirvin avatar janne252 avatar lightrabbit avatar lolleko avatar martinjlowm avatar perryvw avatar pilaoda avatar rhazarian avatar scarf005 avatar thatcosmonaut avatar thejustinwalsh avatar thelartians avatar tomblind avatar wildbook avatar yancouto avatar yoryan avatar z3rio avatar zamiell avatar zapp-brannigan-dota avatar zengjie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

typescripttolua's Issues

Empty return throws exception

function xd() {
  return
}
Compiler.js:60
                    throw exception;
                    ^
TypeError: Cannot read property 'kind' of undefined
    at LuaTranspiler.transpileExpression (Transpiler.js:309:21)
    at LuaTranspiler.transpileReturn (Transpiler.js:306:33)
    at LuaTranspiler.transpileNode (Transpiler.js:92:43)

Operator 'new' declaration error. Undefined behavior and Crash.

I made a mistake and write in TS code file:

class A {}
export let a = new A

Transpiler always crashed with log:

E:\Defold\DasGame>tstl tests.ts
C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Compiler.js:57
                    throw exception;
                    ^

TypeError: Cannot read property 'forEach' of undefined
    at LuaTranspiler.transpileArguments (C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Transpiler.js:899:16)
    at LuaTranspiler.transpileNewExpression (C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Transpiler.js:756:27)
    at LuaTranspiler.transpileExpression (C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Transpiler.js:535:29)
    at LuaTranspiler.transpileVariableDeclaration (C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Transpiler.js:1006:34)
    at C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Transpiler.js:996:29
    at Array.forEach (<anonymous>)
    at LuaTranspiler.transpileVariableStatement (C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Transpiler.js:995:43)
    at LuaTranspiler.transpileNode (C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Transpiler.js:157:26)
    at C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript-to-lua\dist\Transpiler.js:129:33
    at visitNodes (C:\Users\dasannikov\AppData\Roaming\npm\node_modules\typescript\lib\typescript.js:13999:30)

It have taken huge amount of time to find my mistake in TS code.
(No Transpiler notifications and Transpiler crash)

Fluent APIs (Method chaining) outputs weird results

Example:

constructor(owner: CDOTA_BaseNPC) {
        this.owner = owner
        this.fsm = new FiniteStateMachine<TestState>(TestState.Idle)
        this.owner.SetContextThink("AI_THINK", () => {
            return this.tree.tick()
        }, 0)

        this.tree = new BehaviorTreeBuilder()
            .selector()
            .action(() => {
                return this.Attack()
            })
            .action(() => {
                return this.MoveTo()
            })
            .end()
            .build()
}
function TestNPC.constructor(self,owner)
    self.owner=owner
    self.fsm=FiniteStateMachine.new(true,TestState.Idle)
    self.owner.SetContextThink(self.owner,"AI_THINK",function()
        return self.tree.tick(self.tree)
    end
,0)
    self.tree=BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)),function()
        return self.Attack(self)
    end
).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)),function()
        return self.Attack(self)
    end
),function()
        return self.MoveTo(self)
    end
).end(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)),function()
        return self.Attack(self)
    end
).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)),function()
        return self.Attack(self)
    end
),function()
        return self.MoveTo(self)
    end
)).build(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)),function()
        return self.Attack(self)
    end
).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)),function()
        return self.Attack(self)
    end
),function()
        return self.MoveTo(self)
    end
).end(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)),function()
        return self.Attack(self)
    end
).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)).action(BehaviorTreeBuilder.new(true).selector(BehaviorTreeBuilder.new(true)),function()
        return self.Attack(self)
    end
),function()
        return self.MoveTo(self)
    end
)))
end

Example 2

        let builder = new BehaviorTreeBuilder()

        this.tree = builder
            .selector()
            .action(() => {
                return this.Attack()
            })
            .action(() => {
                return this.MoveTo()
            })
            .end()
            .build()
function TestNPC.constructor(self,owner)
    self.owner=owner
    self.fsm=FiniteStateMachine.new(true,TestState.Idle)
    self.owner.SetContextThink(self.owner,"AI_THINK",function()
        return self.tree.tick(self.tree)
    end
,0)
    local builder = BehaviorTreeBuilder.new(true)
    self.tree=builder.selector(builder).action(builder.selector(builder),function()
        return self.Attack(self)
    end
).action(builder.selector(builder).action(builder.selector(builder),function()
        return self.Attack(self)
    end
),function()
        return self.MoveTo(self)
    end
).end(builder.selector(builder).action(builder.selector(builder),function()
        return self.Attack(self)
    end
).action(builder.selector(builder).action(builder.selector(builder),function()
        return self.Attack(self)
    end
),function()
        return self.MoveTo(self)
    end
)).build(builder.selector(builder).action(builder.selector(builder),function()
        return self.Attack(self)
    end
).action(builder.selector(builder).action(builder.selector(builder),function()
        return self.Attack(self)
    end
),function()
        return self.MoveTo(self)
    end
).end(builder.selector(builder).action(builder.selector(builder),function()
        return self.Attack(self)
    end
).action(builder.selector(builder).action(builder.selector(builder),function()
        return self.Attack(self)
    end
),function()
        return self.MoveTo(self)
    end
)))
end

Default parameters

i hope can use default value, just like this

function test(n: number = 0) { }

Unpacking return values?

So I created typescript definitions for MTA:SA (A multiplayer mod for GTA:San Andreas). I'm fairly new to typescript so excuse me if I did something wrong.

This is my function declaration:

declare function getElementPosition(theElement: element): [number, number, number]

This is how I use it:

let [pX, pY, pZ] = getElementPosition(player);

Example from the wiki says:

local x,y,z = getElementPosition( myElegy )

https://wiki.multitheftauto.com/wiki/GetElementPosition

But the transpiler is making this:

local __destr0 = getElementPosition(player)
local pX = __destr0[1]
local pY = __destr0[2]
local pZ = __destr0[3]

which results in the error "attempt to index local '__destr0' (a number value)

compilerOption: dontRequireLualib

First of all, this is an incredibly useful tool! Thank you so much for creating it.

A bit of background/thoughts before I start asking for things:

I've been using lua for a bit over 6 years now (mainly as the scripting language for Company of Heroes >1 and 2). Lack of static types has been a limiting factor.
I'm aware therte are typed variants of Lua but those usually won't solve the other half of the problem: >Editor/IDE support. TypeScript has excellent support on VS Code which makes it the perfect transpile->to-<insert language here> language.

The issue/request:
Looks like the option dontRequireLualib is at least a planned feature since it works if you add it to the tstlOptions configuration:

        'dontRequireLualib': {
            alias: 'dontRequireLualib',
            describe: '',
            default: false,
            type: 'boolean'
        }

(Otherwise the process gets terminated without specifying the reason)

Looks like the most recent pull request is about to touch the compiler options, perhaps this feature could be snuck in? I suppose I could submit a pull request for this too.

Why?
This is an important functionality in CoH1's/CoH2's enbedded & customized lua enviroment since the developers provided a custom function for importing libraries. The use case is extremely narrow but since it's 99% implemented, I thought I'd ask.

Improve lualib integration

Right now lualib is a bit hidden in the dist/ folder and will be even lesss visible in npm packages. Therefore we should automatically require lualib into projects. For that we will need to include the typings (lib-typescript.d.ts) when typescript is parsing the source file (before compilation).

Then we need to add the lualib functions to the output (during compilation), 2 possible approaches for that are:

  1. Keep track of functions/classes from lualib that are used and just copy them into the Lua source file where they are used e.g: local TS_Splice = function() .....

  2. Copy typescript.lua to output root directory of the compiled project. and require('typescript') in each file where the lib is used. (It would be better if we changed typescript.lua to a module structure that way we can do local = require, and utilise the module cache)

Are typings supported?

Say I want to write some TS type definitions for a Lua API -- is this supported in the current state of the transpiler? Also, would I be able to rename methods?

By the way, this project looks really neat! Thank you for the effort!

For-loop translations are inaccurate.

Issue:
for (let i = 0; i < n; i++) and for (let i = 0; i > n; i++) translate to the same thing.

This indicates a pretty serious issue, we should probably rewrite ForHelper.ts

Any way to add additional lua code to output file.

Basically I need to add my lua library to output lua file. It lead to universal way - use special comments. Any lua code from this TypeScript comments will go to output file.

TypeScript:

//@lua require("mylualib")

//@lua function delete_script()
//@lua   print("Delete script")   
//@lua   delete()
//@lua end

declare function delete_script(): void

mylualib_function()
delete_script()

Output Lua:

require("typescript_lualib")

require("mylualib")
function delete_script()
  print("Delete script")   
  delete()
end

mylualib_function()
delete_script()

Defold game engine scripting.

Hi, I just have tried to use TypeScript and the transpiller to script Defold engine (lua scripting originaly) and it works well. Cool! Will investigate further and add comments to this thread.

2018-03-31 14 24 17
2018-03-31 14 26 27

Remove dependencies

We currently have dependencies that are not really required (only used in 1 or 2 locations), we should try to remove those.

  • Remove fs-extra (implement own dir remove).
  • Replace yargs with minimist (or custom parser) and do validation and helptext ourself.
  • Dedent (Probably no longer needed with #74).

Experimental feature: Alias calls

Related pull request: #55

You can define a type alias as follows:

type C_DOTA_BaseNPC = CDOTA_BaseNPC;

Then an explicit cast would cause the transpiler to use the alias instead of the original class. For example

unit.GetMana()                      ==>   CDOTA_BaseNPC.GetMana(unit)
(<C_DOTA_BaseNpc>unit).GetMana()    ==>   C_DOTA_BaseNPC.GetMana(unit)

From now on this experimental feature will be merged into the main branch to test. Please report any issues with this mechanic here.

This feature will remain open until this experimental feature is reverted or added permanently.

Add identifier aliases

This is somewhat related to #56. It would be useful to be able to re-name various identifiers such as class names, method names, etc. in definition files. Perhaps this could be done using either a native TypeScript feature or via custom decorators.

Improve module system

Right now all classes are in the global scope. I dont think this is ideal if you work with many different modules/classes.
The Transpiler should probably convert the export / export default keyword to a return statement. Lua module system.

export default class SomeClass {
  ...
}

=>

local SomeClass = SomeClass or {}
SomeClass.__index = SomeClass
function SomeClass.new(construct, ...)
    local instance = setmetatable({}, SomeClass)
    if construct and SomeClass.constructor then SomeClass.constructor(instance, ...) end
    return instance
end

...

return SomeClass

that way you could require it like this in lua/ts

local SomeClass = require("some_class")

Hashmap created from Interface do not compile correctly

Hashmap created from interface do not compile correctly. Here is example.

Typescript code:

// First test

let myHash1: {[id: string]: string} = {}
myHash1["first"] = "First string"
let a = myHash1["first"]


// Second test

declare interface Dictionary<T> {
    [id: string]: T
}

let myHash2: Dictionary<string> = {}
myHash2["first"] = "First string"
let b = myHash2["first"]

Output Lua code. Error in second test.

require("typescript_lualib")
local myHash1 = {}

myHash1["first"]="First string"
local a = myHash1["first"]

local myHash2 = {}

myHash2["first"+1]="First string" --<< ERROR
local b = myHash2["first"+1] --<< ERROR

Error indexing tuple types

e.g. let a: [number, string] = [1, "WASD"]; let b = a[0]; causes the following error:

c:\Users\User\Documents\TypescriptToLua\dist\Compiler.js:59
                    throw exception;
                    ^

TypeError: Cannot read property 'escapedName' of undefined
    at Function.TSHelper.isArrayType (c:\Users\User\Documents\TypescriptToLua\dist\TSHelper.js:35:65)
    at LuaTranspiler.transpileElementAccessExpression (c:\Users\User\Documents\TypescriptToLua\dist\Transpiler.js:561:43)
    at LuaTranspiler.transpileExpression (c:\Users\User\Documents\TypescriptToLua\dist\Transpiler.js:295:29)
    at LuaTranspiler.transpileVariableDeclaration (c:\Users\User\Documents\TypescriptToLua\dist\Transpiler.js:584:34)
    at c:\Users\User\Documents\TypescriptToLua\dist\Transpiler.js:574:29
    at Array.forEach (<anonymous>)
    at LuaTranspiler.transpileVariableStatement (c:\Users\User\Documents\TypescriptToLua\dist\Transpiler.js:573:43)
    at LuaTranspiler.transpileNode (c:\Users\User\Documents\TypescriptToLua\dist\Transpiler.js:75:43)
    at c:\Users\User\Documents\TypescriptToLua\dist\Transpiler.js:55:33
    at visitNodes (C:\Users\User\AppData\Roaming\npm\node_modules\typescript\lib\typescript.js:12699:30)

Tuple types apparently don't have a symbol property. Not sure what would be a good way to check for tuples.

Add Enum.toString() or something similiar

Right now its not possible to use enums as object/table keys.

Possible Fix

enum Test {
  file,
  directory,
  link
}

let obj {[name: string]: boolean}

function setValue(value : Test) {
  obj[value.toString()] = true
}

-- In Lua
function setValue(value)
  obj[value] = true
end

This would work. But technically its not correct because Test.toString() should return "file", "directory" or "link". With the above solution it would just use the value instead. So this would only work if the enum's values are unique (not sure if thats required by TS or if you can have multiple entries with same values)

Add the Ability to extend native Lua classes/modules

Right now it is not possible to extend table (classes) or modules that already exist in Lua, the output would just overwrite them.

Examples use cases:

// classes
CDOTA_PlayerResource

// modules
table, math, os ...

I can think of 2 possible Solutions:

  1. Find something, elegant similar to:
// Extend the type, and declare the new function
interface Date {
    getWeekNumber(): number;
}

// Add the implementation
Date.prototype.getWeekNumber = function () {
	return 1;
};

I don't know TS well enough to find something like this. But I don't think this will be possible at all because, CDOTA_PlayerResource for example only exists as type in TS when compiling, the value only exists in Lua.

  1. Annotations
class testB extends /*luatable*/ testA {
}

// or

class testC /*luatable*/ {
}

// or for modules

class testD /*luamodule*/ {
}

Missing constructor in super class can result in an error

like this

class A { }
class B extends A { constructor() { super() } }
new B();

translate to lua

A = A or {}
A.__index = A
function A.new(construct, ...)
    local instance = setmetatable({}, A)
    if construct and A.constructor then A.constructor(instance, ...) end
    return instance
end
B = B or A.new()
B.__index = B
B.__base = A
function B.new(construct, ...)
    local instance = setmetatable({}, B)
    if construct and B.constructor then B.constructor(instance, ...) end
    return instance
end
function B.constructor(self)
    self.__base.constructor(self)
end
B.new(true)

because 'A' no 'constructor' function . so self.__base.constructor(self) will cause error;

Add project to npm

Would be easier than having to copy the files from dist/releases every time.

Destructuring assignment

Relevant pages:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
https://www.typescriptlang.org/docs/handbook/variable-declarations.html

This can be supported by creating temporary variables and then doing assignment from those variables, much like how the regular TypeScript compiler does it.
e.g. let [a, b] = [1, 2] could translate to local _a = {1, 2}; local a = _a[1]; local b = _a[2] or separate the declaration of the variables to another line if possible, to avoid having local a bunch of times.

let [a, b] = [1, 2] could also technically translate to local a, b = 1, 2, but I'm not sure trying to recognise when multiple assignment can be used would be worth it.

Supporting ...rest would probably need some helper function, since Lua doesn't have an equivalent to JavaScript's Array.prototype.slice.

Variable declarations are always local

I tried to include JSON data by having a config.ts with let config = {};, but that transpiles to local config = {}.
Then when you do require('config'); nothing happens and config is undefined. I fixed this by removing the local part:

    LuaTranspiler.prototype.transpileVariableDeclaration = function (node) {
        // Find variable identifier
        var identifier = node.name;
        if (node.initializer) {
            var value = this.transpileExpression(node.initializer);
            return "" + identifier.escapedText + " = " + value;
        }
        else {
            return "" + identifier.escapedText + " = nil";
        }
    };

Namespace transpilation incorrect

When transpiling a Typescript namespace that contains functions the resulting Lua does not correctly set their access to local.

Example:
image
Generates to:
image

The generated Lua defines the namespaces' functions globally, they should each be local within the closure.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.