[type-compiler] function __types should be hoisted?
I was using @deepkit/type and found a problem with function hoisting reflections.
import { ReflectionFunction } from "@deepkit/type";
ReflectionFunction.from(system).getParameters(); // returns: []
function system(foo: string) {
return foo
}
So I made a fork and tried to fix the issue. It kinda worked:
import { ReflectionFunction } from "@deepkit/type";
ReflectionFunction.from(system).getParameters(); // returns: [ ReflectionParameter { parameter: { kind ...
function system(foo: string) {
return foo
}
But now, this is a issue:
import { ReflectionFunction } from "@deepkit/type";
console.log(ReflectionFunction.from(system).getParameters()); // ReferenceError: Cannot access '__ΩFoo' before initialization
class Bar {}
type Foo = Bar
function system(foo: Foo) {
return foo
}
So should we hoist everything to make it work? or leave it be?
good question. if we start to hoist function types like this, we have also to hoist all __Ω expressions. the easiest way to do that is to remove NodeFlags.Const in createVariableStatement statements in compiler.ts so that it emits var which is hoisted per default automatically
Thanks for the reply. I will give it a shot on my fork!
Following your advice, I managed to make it work.
import { ReflectionFunction } from "@deepkit/type";
ReflectionFunction.from(system).getParameters(); // returns: [ ReflectionParameter { parameter: { kind ...
class Bar {}
type Foo = Bar
function system(foo: Foo) {
return foo
}
But now, since function declarations are just hoisted to the top of the file instead of the top of the scope, this became a problem.
import { ReflectionFunction } from "@deepkit/type";
ReflectionFunction.from(system).getParameters(); // returns: []
class Bar {}
type Foo = Bar
function system(foo: Foo) {
return foo
}
if (true) {
ReflectionFunction.from(system).getParameters(); // returns: []
function system() {
console.log("Hello, world!")
}
}
Most of linters (at the least the popular ones that I use) don't recoment you do function redefinitions insides scopes so I let you judge if this is a big deal or not. I also have a transformer proof of concept that hoists things to the top of the scope instead of the top of the file:
import * as ts from "typescript";
const helloWorldStatement = ts.factory.createExpressionStatement(
ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier("console"),
ts.factory.createIdentifier("log"),
),
undefined,
[ts.factory.createStringLiteral("Hello, world!")],
),
);
export const transformer = (context: ts.TransformationContext) => {
const statementsToHoist: ts.Statement[][] = [];
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
if (ts.isBlock(node) || ts.isSourceFile(node)) {
statementsToHoist.push([]);
const updatedNode = ts.visitEachChild(node, visitor, context);
const hoisted = statementsToHoist.pop();
if (ts.isBlock(updatedNode)) {
return context.factory.updateBlock(updatedNode, [
...hoisted!,
...updatedNode.statements,
]);
}
if (ts.isSourceFile(updatedNode)) {
return context.factory.updateSourceFile(updatedNode, [
...hoisted!,
...updatedNode.statements,
]);
}
return updatedNode;
}
if (ts.isFunctionDeclaration(node)) {
statementsToHoist[statementsToHoist.length - 1].push(helloWorldStatement);
}
return ts.visitEachChild(node, visitor, context);
};
return (sourceFile: ts.SourceFile) => {
return ts.visitNode(sourceFile, visitor);
};
};