Description
The idea is to instrument JavaScript code and collect types information from the runtime. For example:
Consider JavaScript code (in ES2015):
File render.js
:
export function renderContact(contact, container) {
let span = document.createElement('span');
span.textContent = `${contact.firstName} ${contact.secondName}`;
container.appendChild(span);
}
Then we use it in file main.js
:
import {renderContact} from "./render";
renderContact({firstName: "Steve", lastName: "Works"}, document.body);
Running this code as-is gives no help, but if it's instrumented then we may callect runtime information. main.js
could be instrumented to the following form using esprima/escodegen or TypeScript's parser API:
import {renderContact} from "./render";
CallExpression({line:3, col:0},
null,
renderContact,
[{firstName: "Steve", lastName: "Works"}, document.body])
CallExpression
body could roughly look like:
function CallExpression(position:Position, callee:any, fn:Function, args:any[) {
// collect types information part
args.forEach(arg=>CollectArgumentTypesInformation(position, arg));
// execution part to conform JavaScript semantics and do not break the running application code
return fn.apply(callee, args);
}
function CollectArgumentTypesInformation(position:Position, arg:any) {
let type={};
Object.keys(arg).forEach(key=>{
type[key]=typeof arg[key];
});
return type;
}
CollectArgumentTypesInformation
for contact
parameter would yield something like:
{
firstName:string,
lastName:string
}
that could be later transformed into interface contact
(name of the interface is based on renderContact
1st parameter name):
interface contact {
firstName:string,
lastName:string
}
Later on this TypeScript type could be injected into the file like render.ts
(next to the render.js
) and then be used to generate d.ts
file using TypeScript's tsc --declaration
. Before running tsc --declaration
author could refactor names for better naming and the instrumentation and types collection tool would respect that changes and wouldn't override them.
position
would be used to source-map .js
and .ts
files. Or it could be something similar, maybe using sourceMaps
project itself.