Skip to content

Commit

Permalink
fix(type-compiler): arrow function receive type (#521)
Browse files Browse the repository at this point in the history
* fix(type-compiler): arrow function receive type

* fix(type-compiler): object property assignment arrow function receive type
  • Loading branch information
marcus-sa authored Jan 8, 2024
1 parent fa74d16 commit 6bfb246
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 10 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 43 additions & 9 deletions packages/type-compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import type {
__String,
ArrayTypeNode,
ArrowFunction,
Block,
Bundle,
CallSignatureDeclaration,
ClassDeclaration,
ClassElement,
ClassExpression,
CompilerHost,
CompilerOptions,
ConciseBody,
ConditionalTypeNode,
ConstructorDeclaration,
ConstructorTypeNode,
Expand Down Expand Up @@ -104,6 +106,7 @@ export function isObject(obj: any): obj is { [key: string]: any } {
const {
visitEachChild,
visitNode,
isPropertyAssignment,
isArrayTypeNode,
isArrowFunction,
isCallExpression,
Expand Down Expand Up @@ -762,7 +765,9 @@ export class ReflectionTransformer implements CustomTransformer {
const index = typeParameters.findIndex(v => getIdentifierName(v.name) === name);

let container: Expression = this.f.createIdentifier('globalThis');
if ((isFunctionDeclaration(node.parent) || isFunctionExpression(node.parent)) && node.parent.name) {
if (isArrowFunction(node.parent)) {
container = this.getArrowFunctionΩPropertyAccessIdentifier(node.parent);
} else if ((isFunctionDeclaration(node.parent) || isFunctionExpression(node.parent)) && node.parent.name) {
container = node.parent.name;
} else if (isMethodDeclaration(node.parent) && isIdentifier(node.parent.name)) {
container = this.f.createPropertyAccessExpression(this.f.createIdentifier('this'), node.parent.name);
Expand Down Expand Up @@ -791,7 +796,7 @@ export class ReflectionTransformer implements CustomTransformer {
} else if (isMethodDeclaration(node) || isConstructorDeclaration(node)) {
return this.injectResetΩ(node);
} else if (isArrowFunction(node)) {
return this.decorateArrow(node);
return this.decorateArrowFunction(this.injectResetΩ(node));
} else if ((isNewExpression(node) || isCallExpression(node)) && node.typeArguments && node.typeArguments.length > 0) {

if (isCallExpression(node)) {
Expand Down Expand Up @@ -1113,15 +1118,42 @@ export class ReflectionTransformer implements CustomTransformer {
return this.compilerOptions.module === ModuleKind.CommonJS ? 'cjs' : 'esm';
}

protected injectResetΩ<T extends FunctionDeclaration | FunctionExpression | MethodDeclaration | ConstructorDeclaration>(node: T): T {
protected getArrowFunctionΩPropertyAccessIdentifier(node: ArrowFunction): Identifier {
let { parent } = (node as any).original || node;
if (isVariableDeclaration(parent) && isIdentifier(parent.name)) {
return parent.name;
} else if (isPropertyAssignment(parent) && isIdentifier(parent.name)) {
const names: string[] = [];
while (parent) {
if (isObjectLiteralExpression(parent)) {
parent = parent.parent;
} else if (isVariableDeclaration(parent)) {
names.unshift(getIdentifierName(parent.name as Identifier));
break;
} else if (isIdentifier(parent.name)) {
names.unshift(getIdentifierName(parent.name));
parent = parent.parent;
} else {
throw new Error('Not implemented ' + parent.kind);
}
}
return this.f.createIdentifier(names.join('.'));
} else {
throw new Error('Unsupported parent ' + parent.kind);
}
}

protected injectResetΩ<T extends FunctionDeclaration | FunctionExpression | MethodDeclaration | ConstructorDeclaration | ArrowFunction>(node: T): T {
let hasReceiveType = false;
for (const param of node.parameters) {
if (param.type && getReceiveTypeParameter(param.type)) hasReceiveType = true;
}
if (!hasReceiveType) return node;

let container: Expression = this.f.createIdentifier('globalThis');
if ((isFunctionDeclaration(node) || isFunctionExpression(node)) && node.name) {
if (isArrowFunction(node)) {
container = this.getArrowFunctionΩPropertyAccessIdentifier(node);
} else if ((isFunctionDeclaration(node) || isFunctionExpression(node)) && node.name) {
container = node.name;
} else if (isMethodDeclaration(node) && isIdentifier(node.name)) {
container = this.f.createPropertyAccessExpression(this.f.createIdentifier('this'), node.name);
Expand All @@ -1137,9 +1169,11 @@ export class ReflectionTransformer implements CustomTransformer {
this.f.createToken(ts.SyntaxKind.EqualsToken),
this.f.createIdentifier('undefined')
));
const body = node.body ? this.f.updateBlock(node.body, [reset, ...node.body.statements]) : undefined;
const body = node.body ? this.f.updateBlock(node.body as Block, [reset, ...(node.body as Block).statements]) : undefined;

if (isFunctionDeclaration(node)) {
if (isArrowFunction(node)) {
return this.f.updateArrowFunction(node, node.modifiers, node.typeParameters, node.parameters, node.type, node.equalsGreaterThanToken, body as ConciseBody) as T;
} else if (isFunctionDeclaration(node)) {
return this.f.updateFunctionDeclaration(node, node.modifiers, node.asteriskToken, node.name,
node.typeParameters, node.parameters, node.type, body) as T;
} else if (isFunctionExpression(node)) {
Expand Down Expand Up @@ -2617,10 +2651,10 @@ export class ReflectionTransformer implements CustomTransformer {
}

/**
* const fn = () => { }
* => const fn = Object.assign(() => {}, {__type: 34})
* const fn = () => {}
* => const fn = __assignType(() => {}, [34])
*/
protected decorateArrow(expression: ArrowFunction) {
protected decorateArrowFunction(expression: ArrowFunction) {
const encodedType = this.getTypeOfType(expression);
if (!encodedType) return expression;

Expand Down
44 changes: 43 additions & 1 deletion packages/type/tests/compiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1246,7 +1246,7 @@ test('fn default argument', () => {
expect(type.kind).toEqual(ReflectionKind.string);
});

test('ReceiveType', () => {
test('ReceiveType standard function', () => {
const code = `
function cast<T>(type: ReceiveType<T>) {
return reflect(type);
Expand All @@ -1255,10 +1255,52 @@ test('ReceiveType', () => {

const js = transpile(code);
console.log('js', js);
expect(js).toContain('type = cast.Ω?.[0]');
expect(js).toContain('cast.Ω = undefined');
expect(js).toContain('cast.Ω = [');

const type = transpileAndReturn(code);
expect(type).toEqual({ kind: ReflectionKind.string });
});

test('ReceiveType arrow function', () => {
const code = `
const cast = <T>(type: ReceiveType<T>) => {
return reflect(type);
}
return cast<string>();`;

const js = transpile(code);
console.log('js', js);
expect(js).toContain('type = cast.Ω?.[0]');
expect(js).toContain('cast.Ω = undefined');
expect(js).toContain('cast.Ω = [');

const type = transpileAndReturn(code);
expect(type).toEqual({ kind: ReflectionKind.string });
});

test('ReceiveType object property assignment arrow function', () => {
const code = `
const obj = {
cast: <T>(type: ReceiveType<T>) => {
return reflect(type);
},
};
return obj.cast<string>();
`;

const js = transpile(code);
console.log('js', js);
expect(js).toContain('type = obj.cast.Ω?.[0]');
expect(js).toContain('obj.cast.Ω = undefined');
expect(js).toContain('obj.cast.Ω = [');

const type = transpileAndReturn(code);
expect(type).toEqual({ kind: ReflectionKind.string });
});


test('generic static', () => {
const code = `
interface Request<T> {
Expand Down

0 comments on commit 6bfb246

Please sign in to comment.