X Tutup
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
12 changes: 8 additions & 4 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -797,11 +797,8 @@ export abstract class LuaTranspiler {
case ts.SyntaxKind.SuperKeyword:
return "self.__base";
case ts.SyntaxKind.TypeAssertionExpression:
// Simply ignore the type assertion
return this.transpileExpression((node as ts.TypeAssertion).expression);
case ts.SyntaxKind.AsExpression:
// Also ignore as casts
return this.transpileExpression((node as ts.AsExpression).expression);
return this.transpileAssertionExpression(node as ts.AssertionExpression);
case ts.SyntaxKind.TypeOfExpression:
return this.transpileTypeOfExpression(node as ts.TypeOfExpression);
case ts.SyntaxKind.EmptyStatement:
Expand Down Expand Up @@ -1694,6 +1691,13 @@ export abstract class LuaTranspiler {
}
}

public transpileAssertionExpression(node: ts.AssertionExpression): string {
this.validateFunctionAssignment(node,
this.checker.getTypeAtLocation(node.expression),
this.checker.getTypeAtLocation(node.type));
return this.transpileExpression(node.expression);
}

public transpileTypeOfExpression(node: ts.TypeOfExpression): string {
const expression = this.transpileExpression(node.expression);
return `(type(${expression}) == "table" and "object" or type(${expression}))`;
Expand Down
54 changes: 50 additions & 4 deletions test/unit/assignments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ export class AssignmentTests {
@TestCase("foo.voidLambdaProp", "Foo.staticLambdaProp", "foo+staticLambdaProp")
@TestCase("foo.voidLambdaProp", "foo.voidMethod", "foo+voidMethod")
@TestCase("func", "(func as (string | ((s: string) => string)))", "foo+func")
@TestCase("func", "<(s: string) => string>lambda", "foo+lambda")
@TestCase("func", "lambda as ((s: string) => string)", "foo+lambda")
@Test("Valid function assignment")
public validFunctionAssignment(func: string, assignTo: string, expectResult: string): void {
const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo}; return ${func}("foo");`;
Expand All @@ -282,10 +284,12 @@ export class AssignmentTests {
@TestCase("function(this: void, s: string) { return s; }", "foo")
@TestCase("func", "foo+func", "string | ((s: string) => string)")
@TestCase("func", "foo+func", "T")
@TestCase("<(s: string) => string>func", "foo+func")
@TestCase("func as ((s: string) => string)", "foo+func")
@Test("Valid function argument")
public validFunctionArgument(func: string, expectResult: string, funcType?: string): void {
if (!funcType) {
funcType = "(s: string) => s";
funcType = "(s: string) => string";
}
const code = `${AssignmentTests.funcAssignTestCode}
function takesFunc<T extends ((s: string) => string)>(fn: ${funcType}) {
Expand All @@ -307,10 +311,12 @@ export class AssignmentTests {
@TestCase("function(this: void, s: string) { return s; }", "foo")
@TestCase("func", "foo+func", "string | ((s: string) => string)")
@TestCase("func", "foo+func", "T")
@TestCase("<(s: string) => string>func", "foo+func")
@TestCase("func as ((s: string) => string)", "foo+func")
@Test("Valid function return")
public validFunctionReturn(func: string, expectResult: string, funcType?: string): void {
if (!funcType) {
funcType = "(s: string) => s";
funcType = "(s: string) => string";
}
const code = `${AssignmentTests.funcAssignTestCode}
function returnsFunc<T extends ((s: string) => string)>(): ${funcType} {
Expand Down Expand Up @@ -383,6 +389,8 @@ export class AssignmentTests {
@TestCase("thisLambda", "Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp")
@TestCase("thisLambda", "thisFunc", "foo+thisFunc")
@TestCase("foo.method", "(foo.method as (string | ((this: Foo, s: string) => string))", "foo+method")
@TestCase("foo.method", "<(this: Foo, s: string) => string>foo.lambdaProp", "foo+lambdaProp")
@TestCase("foo.method", "foo.lambdaProp as ((this: Foo, s: string) => string)", "foo+lambdaProp")
@Test("Valid method assignment")
public validMethodAssignment(func: string, assignTo: string, expectResult: string): void {
const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo}; return ${func}("foo");`;
Expand All @@ -401,6 +409,8 @@ export class AssignmentTests {
@TestCase("function(this: Foo, s: string) { return s; }", "foo")
@TestCase("foo.method", "foo+method", "string | ((this: Foo, s: string) => string)")
@TestCase("foo.method", "foo+method", "T")
@TestCase("<(this: Foo, s: string) => string>foo.method", "foo+method")
@TestCase("foo.method as ((this: Foo, s: string) => string)", "foo+method")
@Test("Valid method argument")
public validMethodArgument(func: string, expectResult: string, funcType?: string): void {
if (!funcType) {
Expand All @@ -427,6 +437,8 @@ export class AssignmentTests {
@TestCase("function(this: Foo, s: string) { return s; }", "foo")
@TestCase("foo.method", "foo+method", "string | ((this: Foo, s: string) => string)")
@TestCase("foo.method", "foo+method", "T")
@TestCase("<(this: Foo, s: string) => string>foo.method", "foo+method")
@TestCase("foo.method as ((this: Foo, s: string) => string)", "foo+method")
@Test("Valid method return")
public validMethodReturn(func: string, expectResult: string, funcType?: string): void {
if (!funcType) {
Expand Down Expand Up @@ -473,6 +485,8 @@ export class AssignmentTests {
@TestCase("Foo.staticLambdaProp", "Foo.thisStaticLambdaProp")
@TestCase("Foo.staticLambdaProp", "function(this: Foo, s: string) { return s; }")
@TestCase("func", "(foo.method as (string | ((this: Foo, s: string) => string)))")
@TestCase("func", "<(s: string) => string>foo.method")
@TestCase("func", "foo.method as ((s: string) => string)")
@Test("Invalid function assignment")
public invalidFunctionAssignment(func: string, assignTo: string): void {
const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo};`;
Expand All @@ -493,7 +507,7 @@ export class AssignmentTests {
@Test("Invalid function argument")
public invalidFunctionArgument(func: string, funcType?: string): void {
if (!funcType) {
funcType = "(s: string) => s";
funcType = "(s: string) => string";
}
const code = `${AssignmentTests.funcAssignTestCode}
declare function takesFunc<T extends ((s: string) => string)>(fn: ${funcType});
Expand All @@ -503,6 +517,18 @@ export class AssignmentTests {
"Unsupported conversion from method to function \"fn\". To fix, wrap the method in an arrow function.");
}

@TestCase("<(s: string) => string>foo.method")
@TestCase("foo.method as ((s: string) => string)")
@Test("Invalid function argument cast")
public invalidFunctionArgumentCast(func: string): void {
const code = `${AssignmentTests.funcAssignTestCode}
declare function takesFunc<T extends ((s: string) => string)>(fn: (s: string) => string);
takesFunc(${func});`;
Expect(() => util.transpileString(code)).toThrowError(
TranspileError,
"Unsupported conversion from method to function. To fix, wrap the method in an arrow function.");
}

@TestCase("foo.method")
@TestCase("foo.lambdaProp")
@TestCase("Foo.thisStaticMethod")
Expand All @@ -512,10 +538,12 @@ export class AssignmentTests {
@TestCase("function(this: Foo, s: string) { return s; }")
@TestCase("foo.method", "string | ((s: string) => string)")
@TestCase("foo.method", "T")
@TestCase("<(s: string) => string>foo.method")
@TestCase("foo.method as ((s: string) => string)")
@Test("Invalid function return")
public invalidFunctionReturn(func: string, funcType?: string): void {
if (!funcType) {
funcType = "(s: string) => s";
funcType = "(s: string) => string";
}
const code = `${AssignmentTests.funcAssignTestCode}
function returnsFunc<T extends ((s: string) => string)>(): ${funcType} {
Expand Down Expand Up @@ -569,6 +597,8 @@ export class AssignmentTests {
@TestCase("thisLambda", "foo.voidLambdaProp")
@TestCase("thisLambda", "function(this: void, s: string) { return s; }")
@TestCase("foo.method", "(func as string | ((s: string) => string))")
@TestCase("foo.method", "<(this: Foo, s: string) => string>func")
@TestCase("foo.method", "func as ((this: Foo, s: string) => string)")
@Test("Invalid method assignment")
public invalidMethodAssignment(func: string, assignTo: string): void {
const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo};`;
Expand Down Expand Up @@ -601,6 +631,20 @@ export class AssignmentTests {
+ "or declare the function with an explicit 'this' parameter.");
}

@TestCase("<(this: Foo, s: string) => string>func")
@TestCase("func as ((this: Foo, s: string) => string)")
@Test("Invalid method argument cast")
public invalidMethodArgumentCast(func: string): void {
const code = `${AssignmentTests.funcAssignTestCode}
declare function takesMethod<T extends ((this: Foo, s: string) => string)>(
meth: (this: Foo, s: string) => string);
takesMethod(${func});`;
Expect(() => util.transpileString(code)).toThrowError(
TranspileError,
"Unsupported conversion from function to method. To fix, wrap the function in an arrow function "
+ "or declare the function with an explicit 'this' parameter.");
}

@TestCase("func")
@TestCase("lambda")
@TestCase("Foo.staticMethod")
Expand All @@ -610,6 +654,8 @@ export class AssignmentTests {
@TestCase("function(this: void, s: string) { return s; }")
@TestCase("func", "string | ((this: Foo, s: string) => string)")
@TestCase("func", "T")
@TestCase("<(this: Foo, s: string) => string>func")
@TestCase("func as ((this: Foo, s: string) => string)")
@Test("Invalid method return")
public invalidMethodReturn(func: string, funcType?: string): void {
if (!funcType) {
Expand Down
X Tutup