You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm writing a small plugin for myself that will automatically insert runtime type checks for any function which I add a @typecheck annotation. It works by inserting calls to my own expect function at the beginning of any function definition. I've been able to successfully get it to insert the calls; however, it always emits an extra nil argument first for this, even when I declare expect to have this: void. I tried some tricks to make the TypeScript compiler see the declaration properly, but these haven't ended up working.
Here's my current code (apologies for the hackery):
Code
import*astsfrom"typescript";import*aststlfrom"typescript-to-lua";typeWriteable<T>={-readonly[PinkeyofT]: T[P]};functionunsynthesize<Textendsts.Node>(node: T): T{letn=nodeasWriteable<T>;n.pos=0;n.end=0;returnn;}functionfillTypeList(args: ts.Expression[],type: ts.TypeNode,context: tstl.TransformationContext): boolean{if(ts.isUnionTypeNode(type)){for(lettoftype.types)if(!fillTypeList(args,t,context))returnfalse;}elseif(ts.isFunctionOrConstructorTypeNode(type)){args.push(unsynthesize(ts.factory.createStringLiteral("function")));}elseif(ts.isArrayTypeNode(type)){args.push(unsynthesize(ts.factory.createStringLiteral("table")));}elseif(ts.isLiteralTypeNode(type)){if(type.literal.kind===ts.SyntaxKind.NullKeyword)args.push(unsynthesize(ts.factory.createStringLiteral("nil")));elseif(type.literal.kind===ts.SyntaxKind.FalseKeyword||type.literal.kind===ts.SyntaxKind.TrueKeyword)args.push(unsynthesize(ts.factory.createStringLiteral("boolean")));elsereturnfalse;}elseif(ts.isParenthesizedTypeNode(type)){returnfillTypeList(args,type.type,context);}elseif(ts.isOptionalTypeNode(type)){if(!fillTypeList(args,type.type,context))returnfalse;args.push(unsynthesize(ts.factory.createStringLiteral("nil")));}elseif(type.kind===ts.SyntaxKind.NullKeyword){args.push(unsynthesize(ts.factory.createStringLiteral("nil")));}elseif(type.kind===ts.SyntaxKind.BooleanKeyword){args.push(unsynthesize(ts.factory.createStringLiteral("boolean")));}elseif(type.kind===ts.SyntaxKind.NumberKeyword){args.push(unsynthesize(ts.factory.createStringLiteral("number")));}elseif(type.kind===ts.SyntaxKind.StringKeyword){args.push(unsynthesize(ts.factory.createStringLiteral("string")));}elseif(type.kind===ts.SyntaxKind.FunctionKeyword){args.push(unsynthesize(ts.factory.createStringLiteral("function")));}elseif(type.kind===ts.SyntaxKind.ObjectKeyword){args.push(unsynthesize(ts.factory.createStringLiteral("table")));}elseif(ts.isTypeReferenceNode(type)){args.push(unsynthesize(ts.factory.createStringLiteral(type.typeName.getText())));}else{context.diagnostics.push({category: ts.DiagnosticCategory.Warning,code: 0,file: type.getSourceFile(),start: type.pos,length: type.end-type.pos,messageText: "Could not construct type name for parameter; no type check will be emitted."})returnfalse;}returntrue;}functionaddTypeChecks(m: ts.MethodDeclaration|ts.FunctionDeclaration,context: tstl.TransformationContext){if(m["jsDoc"]){letjsDoc=m["jsDoc"]asts.JSDoc[];if(jsDoc[0].tags?.find(v=>v.tagName.escapedText==="typecheck")){letadd: ts.Statement[]=[];for(letainm.parameters){letarg=m.parameters[a];if(arg.type&&!ts.isThisTypeNode(arg.type)){letargs: ts.Expression[]=[unsynthesize(ts.factory.createNumericLiteral(parseInt(a)+1)),unsynthesize(ts.factory.createIdentifier(arg.name.getText()))];if(fillTypeList(args,arg.type,context)){letid=unsynthesize(ts.factory.createIdentifier("expect"))asWriteable<ts.Identifier>;letcall=unsynthesize(ts.factory.createCallExpression(id,[unsynthesize(ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),unsynthesize(ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),unsynthesize(ts.factory.createRestTypeNode(unsynthesize(ts.factory.createArrayTypeNode(unsynthesize(ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword))))))],args))asWriteable<ts.CallExpression>;for(letnofcall.arguments)(nasWriteable<ts.Expression>).parent=call;id.parent=call;call.flags&=~ts.NodeFlags.Synthesized;letstat=unsynthesize(ts.factory.createExpressionStatement(call))asWriteable<ts.Statement>;call.parent=stat;stat.parent=m;add.push(stat);}}}m.body?.statements["unshift"](...add);}}}classTypeCheckPluginimplementststl.Plugin{publicvisitors={[ts.SyntaxKind.FunctionDeclaration]: (node: ts.FunctionDeclaration,context: tstl.TransformationContext): tstl.Statement[]=>{addTypeChecks(node,context);returncontext.superTransformStatements(node);},[ts.SyntaxKind.ClassDeclaration]: (node: ts.ClassDeclaration,context: tstl.TransformationContext): tstl.Statement[]=>{for(letmofnode.members){if(ts.isMethodDeclaration(m)){addTypeChecks(m,context);}}returncontext.superTransformStatements(node);}}}constplugin=newTypeCheckPlugin();exportdefaultplugin;
I'd appreciate any help in making this work - I'm hoping someone knows enough about the TypeScript compiler to be able to work this out. If it's not possible, it would be nice to have some sort of override to force synthesized calls to not have a this argument.
The text was updated successfully, but these errors were encountered:
There are two main ways TSTL detects functions without this:
The function has a first parameter with parameter name ts.createThisKeyword() with type void. You could try adding this as first parameter to your synthesized function.
I'm writing a small plugin for myself that will automatically insert runtime type checks for any function which I add a
@typecheck
annotation. It works by inserting calls to my ownexpect
function at the beginning of any function definition. I've been able to successfully get it to insert the calls; however, it always emits an extranil
argument first forthis
, even when I declareexpect
to havethis: void
. I tried some tricks to make the TypeScript compiler see the declaration properly, but these haven't ended up working.Here's my current code (apologies for the hackery):
Code
I'd appreciate any help in making this work - I'm hoping someone knows enough about the TypeScript compiler to be able to work this out. If it's not possible, it would be nice to have some sort of override to force synthesized calls to not have a
this
argument.The text was updated successfully, but these errors were encountered: