@@ -942,8 +942,12 @@ export abstract class LuaTranspiler {
942942 return this . transpileSetAccessor ( node . left as ts . PropertyAccessExpression , rhs ) ;
943943 }
944944
945+ // Validate assignment
946+ const rightType = this . checker . getTypeAtLocation ( node . right ) ;
947+ const leftType = this . checker . getTypeAtLocation ( node . left ) ;
948+ this . validateAssignment ( node . right , rightType , leftType ) ;
949+
945950 if ( ts . isArrayLiteralExpression ( node . left ) ) {
946- // Destructing assignment
947951 const vars = node . left . elements . map ( e => this . transpileExpression ( e ) ) . join ( "," ) ;
948952 const vals = tsHelper . isTupleReturnCall ( node . right , this . checker )
949953 ? rhs : this . transpileDestructingAssignmentValue ( node . right ) ;
@@ -1144,7 +1148,8 @@ export abstract class LuaTranspiler {
11441148
11451149 public transpileNewExpression ( node : ts . NewExpression ) : string {
11461150 const name = this . transpileExpression ( node . expression ) ;
1147- let params = node . arguments ? this . transpileArguments ( node . arguments , ts . createTrue ( ) ) : "true" ;
1151+ const sig = this . checker . getResolvedSignature ( node ) ;
1152+ const params = node . arguments ? this . transpileArguments ( node . arguments , sig , ts . createTrue ( ) ) : "true" ;
11481153 const type = this . checker . getTypeAtLocation ( node ) ;
11491154 const classDecorators = tsHelper . getCustomDecorators ( type , this . checker ) ;
11501155
@@ -1159,15 +1164,7 @@ export abstract class LuaTranspiler {
11591164 if ( ! customDecorator . args [ 0 ] ) {
11601165 throw TSTLErrors . InvalidDecoratorArgumentNumber ( "!CustomConstructor" , 0 , 1 , node ) ;
11611166 }
1162- if ( ! ts . isPropertyAccessExpression ( node . expression )
1163- && ! ts . isElementAccessExpression ( node . expression )
1164- && ! tsHelper . getCustomDecorators ( type , this . checker ) . has ( DecoratorKind . NoContext ) ) {
1165- const context = this . isStrict ? ts . createNull ( ) : ts . createIdentifier ( "_G" ) ;
1166- params = this . transpileArguments ( node . arguments , context ) ;
1167- } else {
1168- params = this . transpileArguments ( node . arguments ) ;
1169- }
1170- return `${ customDecorator . args [ 0 ] } (${ params } )` ;
1167+ return `${ customDecorator . args [ 0 ] } (${ this . transpileArguments ( node . arguments ) } )` ;
11711168 }
11721169
11731170 return `${ name } .new(${ params } )` ;
@@ -1190,22 +1187,25 @@ export abstract class LuaTranspiler {
11901187 ? `({ ${ result } })` : result ;
11911188 }
11921189
1190+ const sig = this . checker . getResolvedSignature ( node ) ;
1191+
11931192 // Handle super calls properly
11941193 if ( node . expression . kind === ts . SyntaxKind . SuperKeyword ) {
1195- params = this . transpileArguments ( node . arguments , ts . createNode ( ts . SyntaxKind . ThisKeyword ) as ts . Expression ) ;
1194+ params = this . transpileArguments ( node . arguments , sig ,
1195+ ts . createNode ( ts . SyntaxKind . ThisKeyword ) as ts . Expression ) ;
11961196 const className = this . classStack [ this . classStack . length - 1 ] ;
11971197 return `${ className } .__base.constructor(${ params } )` ;
11981198 }
11991199
1200- callPath = this . transpileExpression ( node . expression ) ;
12011200 const type = this . checker . getTypeAtLocation ( node . expression ) ;
1202- if ( ! ts . isPropertyAccessExpression ( node . expression )
1203- && ! ts . isElementAccessExpression ( node . expression )
1204- && ! tsHelper . getCustomDecorators ( type , this . checker ) . has ( DecoratorKind . NoContext ) ) {
1201+ callPath = this . transpileExpression ( node . expression ) ;
1202+ if ( tsHelper . isFunctionWithContext ( type , this . checker )
1203+ && ! ts . isPropertyAccessExpression ( node . expression )
1204+ && ! ts . isElementAccessExpression ( node . expression ) ) {
12051205 const context = this . isStrict ? ts . createNull ( ) : ts . createIdentifier ( "_G" ) ;
1206- params = this . transpileArguments ( node . arguments , context ) ;
1206+ params = this . transpileArguments ( node . arguments , sig , context ) ;
12071207 } else {
1208- params = this . transpileArguments ( node . arguments ) ;
1208+ params = this . transpileArguments ( node . arguments , sig ) ;
12091209 }
12101210 return isTupleReturn && ! isTupleReturnForward && ! isInDestructingAssignment && returnValueIsUsed
12111211 ? `({ ${ callPath } (${ params } ) })` : `${ callPath } (${ params } )` ;
@@ -1248,10 +1248,13 @@ export abstract class LuaTranspiler {
12481248 return this . transpileFunctionCallExpression ( node ) ;
12491249 }
12501250
1251+ const sig = this . checker . getResolvedSignature ( node ) ;
1252+
12511253 // Get the type of the function
12521254 if ( node . expression . expression . kind === ts . SyntaxKind . SuperKeyword ) {
12531255 // Super calls take the format of super.call(self,...)
1254- params = this . transpileArguments ( node . arguments , ts . createNode ( ts . SyntaxKind . ThisKeyword ) as ts . Expression ) ;
1256+ params = this . transpileArguments ( node . arguments , sig ,
1257+ ts . createNode ( ts . SyntaxKind . ThisKeyword ) as ts . Expression ) ;
12551258 return `${ this . transpileExpression ( node . expression ) } (${ params } )` ;
12561259 } else {
12571260 // Replace last . with : here
@@ -1260,13 +1263,13 @@ export abstract class LuaTranspiler {
12601263 return `tostring(${ this . transpileExpression ( node . expression . expression ) } )` ;
12611264 } else if ( name === "hasOwnProperty" ) {
12621265 const expr = this . transpileExpression ( node . expression . expression ) ;
1263- params = this . transpileArguments ( node . arguments ) ;
1266+ params = this . transpileArguments ( node . arguments , sig ) ;
12641267 return `(rawget(${ expr } , ${ params } )~=nil)` ;
12651268 } else {
12661269 const type = this . checker . getTypeAtLocation ( node . expression ) ;
1267- const op = tsHelper . getCustomDecorators ( type , this . checker ) . has ( DecoratorKind . NoContext ) ? ". " : ": " ;
1270+ const op = tsHelper . isFunctionWithContext ( type , this . checker ) ? ": " : ". " ;
12681271 callPath = `${ this . transpileExpression ( node . expression . expression ) } ${ op } ${ name } ` ;
1269- params = this . transpileArguments ( node . arguments ) ;
1272+ params = this . transpileArguments ( node . arguments , sig ) ;
12701273 return `${ callPath } (${ params } )` ;
12711274 }
12721275 }
@@ -1387,6 +1390,10 @@ export abstract class LuaTranspiler {
13871390
13881391 public transpileFunctionCallExpression ( node : ts . CallExpression ) : string {
13891392 const expression = node . expression as ts . PropertyAccessExpression ;
1393+ const callerType = this . checker . getTypeAtLocation ( expression . expression ) ;
1394+ if ( ! tsHelper . isFunctionWithContext ( callerType , this . checker ) ) {
1395+ throw TSTLErrors . UnsupportedMethodConversion ( node ) ;
1396+ }
13901397 const params = this . transpileArguments ( node . arguments ) ;
13911398 const caller = this . transpileExpression ( expression . expression ) ;
13921399 const expressionName = this . transpileIdentifier ( expression . name ) ;
@@ -1402,17 +1409,51 @@ export abstract class LuaTranspiler {
14021409 }
14031410 }
14041411
1405- public transpileArguments ( params : ts . NodeArray < ts . Expression > , context ?: ts . Expression ) : string {
1412+ public validateAssignment ( node : ts . Node , fromType : ts . Type , toType : ts . Type ) : void {
1413+ if ( ( toType . flags & ts . TypeFlags . Any ) !== 0 ) {
1414+ // Assigning to un-typed variable
1415+ return ;
1416+ } else if ( ( fromType as ts . TypeReference ) . typeArguments && ( toType as ts . TypeReference ) . typeArguments ) {
1417+ // Recurse into tuples/arrays
1418+ ( toType as ts . TypeReference ) . typeArguments . forEach ( ( t , i ) => {
1419+ this . validateAssignment ( node , ( fromType as ts . TypeReference ) . typeArguments [ i ] , t ) ;
1420+ } ) ;
1421+ } else {
1422+ // Check function assignments
1423+ const fromHasContext = tsHelper . isFunctionWithContext ( fromType , this . checker ) ;
1424+ const toHasContext = tsHelper . isFunctionWithContext ( toType , this . checker ) ;
1425+ if ( fromHasContext !== toHasContext ) {
1426+ if ( fromHasContext ) {
1427+ throw TSTLErrors . UnsupportedFunctionConversion ( node ) ;
1428+ } else {
1429+ throw TSTLErrors . UnsupportedMethodConversion ( node ) ;
1430+ }
1431+ }
1432+ }
1433+ }
1434+
1435+ public transpileArguments ( params : ts . NodeArray < ts . Expression > , sig ?: ts . Signature ,
1436+ context ?: ts . Expression ) : string {
14061437 const parameters : string [ ] = [ ] ;
14071438
14081439 // Add context as first param if present
14091440 if ( context ) {
14101441 parameters . push ( this . transpileExpression ( context ) ) ;
14111442 }
14121443
1413- params . forEach ( param => {
1414- parameters . push ( this . transpileExpression ( param ) ) ;
1415- } ) ;
1444+ if ( sig && sig . parameters . length >= params . length ) {
1445+ for ( let i = 0 ; i < params . length ; ++ i ) {
1446+ const param = params [ i ] ;
1447+ const paramType = this . checker . getTypeAtLocation ( param ) ;
1448+ const sigType = this . checker . getTypeAtLocation ( sig . parameters [ i ] . valueDeclaration ) ;
1449+ this . validateAssignment ( param , paramType , sigType ) ;
1450+ parameters . push ( this . transpileExpression ( param ) ) ;
1451+ }
1452+ } else {
1453+ params . forEach ( param => {
1454+ parameters . push ( this . transpileExpression ( param ) ) ;
1455+ } ) ;
1456+ }
14161457
14171458 return parameters . join ( "," ) ;
14181459 }
@@ -1592,6 +1633,13 @@ export abstract class LuaTranspiler {
15921633 }
15931634
15941635 public transpileVariableDeclaration ( node : ts . VariableDeclaration ) : string {
1636+ if ( node . initializer ) {
1637+ // Validate assignment
1638+ const initializerType = this . checker . getTypeAtLocation ( node . initializer ) ;
1639+ const varType = this . checker . getTypeFromTypeNode ( node . type ) ;
1640+ this . validateAssignment ( node . initializer , initializerType , varType ) ;
1641+ }
1642+
15951643 if ( ts . isIdentifier ( node . name ) ) {
15961644 // Find variable identifier
15971645 const identifierName = this . transpileIdentifier ( node . name ) ;
@@ -1635,7 +1683,7 @@ export abstract class LuaTranspiler {
16351683 const methodName = this . transpileIdentifier ( node . name ) ;
16361684
16371685 const type = this . checker . getTypeAtLocation ( node ) ;
1638- const context = tsHelper . getCustomDecorators ( type , this . checker ) . has ( DecoratorKind . NoContext ) ? null : "self" ;
1686+ const context = tsHelper . isFunctionWithContext ( type , this . checker ) ? "self" : null ;
16391687 const [ paramNames , spreadIdentifier ] = this . transpileParameters ( node . parameters , context ) ;
16401688
16411689 let prefix = this . accessPrefix ( node ) ;
@@ -1672,6 +1720,9 @@ export abstract class LuaTranspiler {
16721720
16731721 // Only push parameter name to paramName array if it isn't a spread parameter
16741722 for ( const param of parameters ) {
1723+ if ( ts . isIdentifier ( param . name ) && param . name . originalKeywordKind === ts . SyntaxKind . ThisKeyword ) {
1724+ continue ;
1725+ }
16751726 const paramName = this . transpileIdentifier ( param . name as ts . Identifier ) ;
16761727
16771728 // This parameter is a spread parameter (...param)
@@ -1718,7 +1769,7 @@ export abstract class LuaTranspiler {
17181769 }
17191770
17201771 const type = this . checker . getTypeAtLocation ( node ) ;
1721- const context = tsHelper . getCustomDecorators ( type , this . checker ) . has ( DecoratorKind . NoContext ) ? null : "self" ;
1772+ const context = tsHelper . isFunctionWithContext ( type , this . checker ) ? "self" : null ;
17221773 const [ paramNames , spreadIdentifier ] = this . transpileParameters ( node . parameters , context ) ;
17231774
17241775 // Build function header
@@ -1918,6 +1969,9 @@ export abstract class LuaTranspiler {
19181969
19191970 public transpileConstructor ( node : ts . ConstructorDeclaration ,
19201971 className : string ) : string {
1972+ // Don't transpile methods without body (overload declarations)
1973+ if ( ! node . body ) { return "" ; }
1974+
19211975 const extraInstanceFields = [ ] ;
19221976
19231977 const parameters = [ "self" ] ;
@@ -1987,8 +2041,10 @@ export abstract class LuaTranspiler {
19872041 }
19882042
19892043 public transpileFunctionExpression ( node : ts . FunctionLikeDeclaration , context : string | null ) : string {
2044+ const type = this . checker . getTypeAtLocation ( node ) ;
2045+ const hasContext = tsHelper . isFunctionWithContext ( type , this . checker ) ;
19902046 // Build parameter string
1991- const [ paramNames , spreadIdentifier ] = this . transpileParameters ( node . parameters , context ) ;
2047+ const [ paramNames , spreadIdentifier ] = this . transpileParameters ( node . parameters , hasContext ? context : null ) ;
19922048 let result = `function(${ paramNames . join ( "," ) } )\n` ;
19932049 this . pushIndent ( ) ;
19942050 const body = ts . isBlock ( node . body ) ? node . body : ts . createBlock ( [ ts . createReturn ( node . body ) ] ) ;
0 commit comments