Skip to content

Commit 451232e

Browse files
committed
Fixed handling unnamed/undeclared returned type from functions
Fixes #27
1 parent 4152e4d commit 451232e

File tree

4 files changed

+142
-2
lines changed

4 files changed

+142
-2
lines changed

src/exports-symbol-tree.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,13 @@ export class ExportsSymbolTree {
5858
}
5959

6060
private computeTreeForChildren(targetSymbolsSet: Set<ts.Symbol>, node: ts.Node, visitedSymbols: Set<ts.Symbol>): void {
61+
const typeChecker = this.program.getTypeChecker();
62+
6163
// it's similar to handling ts.Block node - both Block and variable's initializer are part of _implementation_
6264
// and we don't care about that implementation at all - we just only need to worry it's definition
6365
// for functions it is arguments and return type
6466
// for variables - the type of a variable
6567
if (ts.isVariableDeclaration(node)) {
66-
const typeChecker = this.program.getTypeChecker();
6768
const variableType = typeChecker.getTypeAtLocation(node);
6869
const variableTypeSymbol = variableType.getSymbol();
6970
if (variableTypeSymbol !== undefined) {
@@ -73,6 +74,15 @@ export class ExportsSymbolTree {
7374
return;
7475
}
7576

77+
if ((ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) && node.type === undefined) {
78+
// this means that a function/method doesn't have a declared returned type so here we need to get calculated one
79+
for (const signature of typeChecker.getTypeAtLocation(node).getCallSignatures()) {
80+
for (const childSymbol of splitTransientSymbol(signature.getReturnType().symbol, typeChecker)) {
81+
targetSymbolsSet.add(childSymbol);
82+
}
83+
}
84+
}
85+
7686
ts.forEachChild(node, (childNode: ts.Node) => this.computeTreeForNode(targetSymbolsSet, childNode, visitedSymbols));
7787
}
7888

src/transformer.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,19 @@ function createTransformerFactory(program: ts.Program, options?: Partial<RenameO
437437
}
438438

439439
if (exportsSymbolTree.isSymbolAccessibleFromExports(nodeSymbol)) {
440-
return putToCache(nodeSymbol, VisibilityType.External)
440+
return putToCache(nodeSymbol, VisibilityType.External);
441441
}
442442

443443
for (const declaration of symbolDeclarations) {
444+
if (ts.isObjectLiteralExpression(declaration.parent)) {
445+
// most likely this is unnamed object/type
446+
// and the best we can do is try to find a symbol for this type in publicly accessible symbols
447+
const typeSymbol = typeChecker.getTypeAtLocation(declaration.parent).symbol;
448+
if (exportsSymbolTree.isSymbolAccessibleFromExports(typeSymbol)) {
449+
return putToCache(nodeSymbol, VisibilityType.External)
450+
}
451+
}
452+
444453
if (!isNodeNamedDeclaration(declaration.parent) || declaration.parent.name === undefined) {
445454
continue;
446455
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
class UnexportedClass {
2+
public method() {
3+
return { a: 123, b: 321 };
4+
}
5+
}
6+
7+
function privateFunctionWithDeclaredType1(): { a: number; b: string } {
8+
const { a } = new UnexportedClass().method();
9+
return {
10+
a,
11+
b: 'str',
12+
};
13+
}
14+
15+
function privateFunctionWithUndeclaredType() {
16+
return {
17+
f: 1,
18+
f2: 2,
19+
};
20+
}
21+
22+
function privateFunctionWithDeclaredType2(): { f: number, f2: number } {
23+
// note that if you extract returned value to a variable it won't work
24+
return {
25+
f: 1,
26+
f2: 2,
27+
};
28+
}
29+
30+
function privateFunctionWhichUsesPublic(): void {
31+
console.log(fn().a, fn1().f, fn2().f2);
32+
}
33+
34+
export class ExportedClass {
35+
public method() {
36+
return { a: 123, b: 321 };
37+
}
38+
39+
private privateMethod() {
40+
return { a: 123, b: 321 };
41+
}
42+
}
43+
44+
export function fn() {
45+
const { a } = privateFunctionWithDeclaredType1();
46+
47+
return {
48+
a,
49+
b: 'str',
50+
};
51+
}
52+
53+
export function fn1() {
54+
return privateFunctionWithUndeclaredType();
55+
}
56+
57+
export function fn2() {
58+
return privateFunctionWithDeclaredType2();
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
exports.fn2 = exports.fn1 = exports.fn = exports.ExportedClass = void 0;
4+
var UnexportedClass = /** @class */ (function () {
5+
function UnexportedClass() {
6+
}
7+
UnexportedClass.prototype._internal_method = function () {
8+
return { _internal_a: 123, _internal_b: 321 };
9+
};
10+
return UnexportedClass;
11+
}());
12+
function privateFunctionWithDeclaredType1() {
13+
var a = new UnexportedClass()._internal_method()._internal_a;
14+
return {
15+
_internal_a: a,
16+
_internal_b: 'str',
17+
};
18+
}
19+
function privateFunctionWithUndeclaredType() {
20+
return {
21+
f: 1,
22+
f2: 2,
23+
};
24+
}
25+
function privateFunctionWithDeclaredType2() {
26+
// note that if you extract returned value to a variable it won't work
27+
return {
28+
f: 1,
29+
f2: 2,
30+
};
31+
}
32+
function privateFunctionWhichUsesPublic() {
33+
console.log(fn().a, fn1().f, fn2().f2);
34+
}
35+
var ExportedClass = /** @class */ (function () {
36+
function ExportedClass() {
37+
}
38+
ExportedClass.prototype.method = function () {
39+
return { a: 123, b: 321 };
40+
};
41+
ExportedClass.prototype._private_privateMethod = function () {
42+
return { _internal_a: 123, _internal_b: 321 };
43+
};
44+
return ExportedClass;
45+
}());
46+
exports.ExportedClass = ExportedClass;
47+
function fn() {
48+
var a = privateFunctionWithDeclaredType1()._internal_a;
49+
return {
50+
a: a,
51+
b: 'str',
52+
};
53+
}
54+
exports.fn = fn;
55+
function fn1() {
56+
return privateFunctionWithUndeclaredType();
57+
}
58+
exports.fn1 = fn1;
59+
function fn2() {
60+
return privateFunctionWithDeclaredType2();
61+
}
62+
exports.fn2 = fn2;

0 commit comments

Comments
 (0)