Skip to content

Commit

Permalink
Improve handling of function-modules created with Object.assign
Browse files Browse the repository at this point in the history
  • Loading branch information
Gerrit0 committed Nov 11, 2023
1 parent c86f4a9 commit 54a3337
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Fixed default option values on options declared by plugins in packages mode, #2433.
- `gitRevision` will now be replaced in `sourceLinkTemplate`, #2434.
- Improved handling of function-modules created with `Object.assign`, #2436.
- Fixed an infinite loop when `skipLibCheck` is used to ignore some compiler errors, #2438.

### Thanks!
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"test": "mocha --config .config/mocha.fast.json",
"test:cov": "c8 mocha --config .config/mocha.fast.json",
"doc:c": "node bin/typedoc --tsconfig src/test/converter/tsconfig.json",
"doc:c2": "node --inspect-brk bin/typedoc --tsconfig src/test/converter2/tsconfig.json",
"doc:c2": "node bin/typedoc --tsconfig src/test/converter2/tsconfig.json",
"test:full": "c8 mocha --config .config/mocha.full.json",
"test:visual": "ts-node ./src/test/capture-screenshots.ts && ./scripts/compare_screenshots.sh",
"test:visual:accept": "node scripts/accept_visual_regression.js",
Expand Down
38 changes: 26 additions & 12 deletions src/lib/converter/symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,18 +178,6 @@ export function convertSymbol(
flags = removeFlag(flags, ts.SymbolFlags.Property);
}

// A default exported function with no associated variable is a property, but
// we should really convert it as a variable for documentation purposes
// export default () => {}
// export default 123
if (
flags === ts.SymbolFlags.Property &&
symbol.name === "default" &&
context.scope.kindOf(ReflectionKind.Module | ReflectionKind.Project)
) {
flags = ts.SymbolFlags.BlockScopedVariable;
}

for (const flag of getEnumFlags(flags ^ allConverterFlags)) {
if (!(flag & allConverterFlags)) {
context.logger.verbose(
Expand Down Expand Up @@ -645,6 +633,14 @@ function convertProperty(
symbol: ts.Symbol,
exportSymbol?: ts.Symbol,
) {
// This might happen if we're converting a function-module created with Object.assign
// or `export default () => {}`
if (
context.scope.kindOf(ReflectionKind.SomeModule | ReflectionKind.Project)
) {
return convertVariable(context, symbol, exportSymbol);
}

const declarations = symbol.getDeclarations() ?? [];

// Don't do anything if we inherited this property and it is private.
Expand Down Expand Up @@ -1019,6 +1015,24 @@ function convertVariableAsFunction(
);
}

// #2436: Functions created with Object.assign on a function won't have a namespace flag
// but likely have properties that we should put into a namespace.
if (
type.getProperties().length &&
!hasAnyFlag(
symbol.flags,
ts.SymbolFlags.NamespaceModule | ts.SymbolFlags.ValueModule,
)
) {
const ns = context.createDeclarationReflection(
ReflectionKind.Namespace,
symbol,
exportSymbol,
);
context.finalizeDeclarationReflection(ns);
convertSymbols(context.withScope(ns), type.getProperties());
}

return ts.SymbolFlags.Property;
}

Expand Down
17 changes: 17 additions & 0 deletions src/test/converter2/issues/gh2436.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/** The FOO function */
function foo() {
return "foo";
}

function bugInner(): { foo: string } {
return { foo: "bar" };
}

export const bug: {
(): { foo: string };
foo: typeof foo;
bar: 42;
} = Object.assign(bugInner, {
foo,
bar: 42 as const,
});
16 changes: 16 additions & 0 deletions src/test/issues.c2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,22 @@ describe("Issue Tests", () => {
);
});

it("Handles function-namespaces created with Object.assign #2436", () => {
const project = convert();
equal(project.children?.map((c) => c.kind), [
ReflectionKind.Namespace,
ReflectionKind.Function,
]);
equal(
project.children[0].getChildByName("bar")?.kind,
ReflectionKind.Variable,
);
equal(
project.children[0].getChildByName("foo")?.kind,
ReflectionKind.Function,
);
});

it("Handles recursive aliases without looping infinitely #2438", () => {
const bad = query(convert(), "Bad");
equal(bad.kind, ReflectionKind.Interface);
Expand Down

0 comments on commit 54a3337

Please sign in to comment.