Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/TSHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class TSHelper {
return statements.some(statement => statement.kind === kind);
}

public static getExtendedType(node: ts.ClassDeclaration, checker: ts.TypeChecker): ts.Type | undefined {
public static getExtendedType(node: ts.ClassLikeDeclarationBase, checker: ts.TypeChecker): ts.Type | undefined {
if (node && node.heritageClauses) {
for (const clause of node.heritageClauses) {
if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
Expand Down
19 changes: 11 additions & 8 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,11 @@ export abstract class LuaTranspiler {
return this.transpileSpreadElement(node as ts.SpreadElement);
case ts.SyntaxKind.NonNullExpression:
return this.transpileExpression((node as ts.NonNullExpression).expression);
case ts.SyntaxKind.ClassExpression:
this.namespace.push("");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason for this line?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It scopes the class declaration. Otherwise the class would be in global scope. Not sure if this is the best approach - I see it as sort of an anonymous namespace in C++.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you provide an input/ouput example?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let f = function() { let a = class{} }
would generate
local f; f = function() local a = (function() --[[ should be local ]] _ = _ or {} _.__index = _ function _.new(construct, ...) local self = setmetatable({}, _) if construct and _.constructor then _.constructor(self, ...) end return self end function _.constructor(self) end ; return _ end)(); end ;

const classDeclaration = this.transpileClass(node as ts.ClassExpression, "_");
this.namespace.pop();
return `(function() ${classDeclaration}; return _ end)()`;
case ts.SyntaxKind.Block:
this.pushIndent();
const ret = "do \n" + this.transpileBlock(node as ts.Block) + "end\n";
Expand Down Expand Up @@ -1596,13 +1601,12 @@ export abstract class LuaTranspiler {
}

// Transpile a class declaration
public transpileClass(node: ts.ClassDeclaration): string {
if (!node.name) {
public transpileClass(node: ts.ClassLikeDeclarationBase, nameOverride?: string): string {
let className = node.name ? this.transpileIdentifier(node.name) : nameOverride;
if (!className) {
throw TSTLErrors.MissingClassName(node);
}

let className = this.transpileIdentifier(node.name);

const decorators = tsHelper.getCustomDecorators(this.checker.getTypeAtLocation(node), this.checker);

// Find out if this class is extension of existing class
Expand Down Expand Up @@ -1648,7 +1652,7 @@ export abstract class LuaTranspiler {
}

if (!isExtension && !isMetaExtension) {
result += this.transpileClassCreationMethods(node, instanceFields, extendsType);
result += this.transpileClassCreationMethods(node, className, instanceFields, extendsType);
} else {
for (const f of instanceFields) {
// Get identifier
Expand Down Expand Up @@ -1697,10 +1701,9 @@ export abstract class LuaTranspiler {
return result;
}

public transpileClassCreationMethods(node: ts.ClassDeclaration, instanceFields: ts.PropertyDeclaration[],
public transpileClassCreationMethods(node: ts.ClassLikeDeclarationBase, className: string,
instanceFields: ts.PropertyDeclaration[],
extendsType: ts.Type): string {
const className = this.transpileIdentifier(node.name);

let noClassOr = false;
if (extendsType) {
const decorators = tsHelper.getCustomDecorators(extendsType, this.checker);
Expand Down
43 changes: 43 additions & 0 deletions test/unit/class.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,47 @@ export class ClassTests {
// Assert
Expect(result).toBe(10);
}
@Test("classExpression")
public classExpression(): void {
const lua = util.transpileString(
`class a {
public method() {
return "instance of a";
}
}
b = class extends a {
public method() {
return "instance of b";
}
}
let inst = new b(6);
return inst.method();`
);

// Execute
const result = util.executeLua(lua);

// Assert
Expect(result).toBe("instance of b");
}
@Test("classExpressionBaseClassMethod")
public classExpressionBaseClassMethod(): void {
const lua = util.transpileString(
`class a {
public method() {
return 42;
}
}
b = class extends a {
}
let inst = new b();
return inst.method();`
);

// Execute
const result = util.executeLua(lua);

// Assert
Expect(result).toBe(42);
}
}