Skip to content

Commit ad7af2b

Browse files
authored
Add support for destructuring arguments (#48)
1 parent 6fd6a61 commit ad7af2b

File tree

3 files changed

+114
-5
lines changed

3 files changed

+114
-5
lines changed

sphinx_js/typedoc.py

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -806,23 +806,80 @@ def return_type(self, converter: Converter) -> list[ir.Return]:
806806
)
807807
]
808808

809+
def _fix_type_suffix(self) -> None:
810+
if self.path[-1] == "__type":
811+
self.path = self.path[:-1]
812+
self.path[-1] = self.path[-1].removesuffix(".")
813+
self.name = self.path[-1]
814+
815+
def _destructure_param(self, param: Param) -> list[Param]:
816+
"""We want to document a destructured argument as if it were several
817+
separate arguments. This finds complex inline object types in the arguments
818+
list of a function and "destructures" them into separately documented arguments.
819+
820+
E.g., a function
821+
822+
/**
823+
* @param options
824+
* @destructure options
825+
*/
826+
function f({x , y } : {
827+
/** The x value */
828+
x : number,
829+
/** The y value */
830+
y : string
831+
}){ ... }
832+
833+
should be documented like:
834+
835+
options.x (number) The x value
836+
options.y (number) The y value
837+
"""
838+
type = param.type
839+
assert isinstance(type, ReflectionType)
840+
decl = type.declaration
841+
result = []
842+
for child in decl.children:
843+
assert isinstance(child, Member)
844+
child_param = Param(
845+
name=param.name + "." + child.name, flags=Flags(), type=child.type
846+
)
847+
result.append(child_param)
848+
return result
849+
850+
def _destructure_params(self) -> list[Param]:
851+
destructure_targets = []
852+
for tag in self.comment.blockTags:
853+
if tag.tag == "@destructure":
854+
destructure_targets = tag.content[0].text.split(" ")
855+
break
856+
857+
if not destructure_targets:
858+
return self.parameters
859+
860+
params = []
861+
for p in self.parameters:
862+
if p.name in destructure_targets:
863+
params.extend(self._destructure_param(p))
864+
return params
865+
809866
def to_ir(
810867
self, converter: Converter
811868
) -> tuple[ir.Function | None, Sequence["Node"]]:
812869
if self.inheritedFrom is not None:
813870
if self.comment == Comment():
814871
return None, []
815-
if self.path[-1] == "__type":
816-
self.path = self.path[:-1]
817-
self.path[-1] = self.path[-1].removesuffix(".")
818-
self.name = self.path[-1]
872+
873+
self._fix_type_suffix()
874+
params = self._destructure_params()
875+
819876
# This is the real meat of a function, method, or constructor.
820877
#
821878
# Constructors' .name attrs end up being like 'new Foo'. They
822879
# should probably be called "constructor", but I'm not bothering
823880
# with that yet because nobody uses that attr on constructors atm.
824881
result = ir.Function(
825-
params=[p.to_ir(converter) for p in self.parameters],
882+
params=[p.to_ir(converter) for p in params],
826883
# Exceptions are discouraged in TS as being unrepresentable in its
827884
# type system. More importantly, TypeDoc does not support them.
828885
exceptions=[],

tests/test_typedoc_analysis/source/types.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,38 @@ export let option: {a: number; b?: string};
145145
export function codeInDescription() {
146146

147147
}
148+
149+
/**
150+
* An example with destructured args
151+
*
152+
* @param options
153+
* @param options.a - The 'a' string.
154+
* @param options.b - The 'b' string.
155+
* @destructure options
156+
*/
157+
export function destructureTest({ a, b }: { a: string; b: { c: string } }) {}
158+
159+
/**
160+
* An example with destructured args
161+
*
162+
* @param options
163+
* @destructure options
164+
*/
165+
export function destructureTest2({
166+
a,
167+
b,
168+
}: {
169+
/** The 'a' string. */
170+
a: string;
171+
/** The 'b' string. */
172+
b: { c: string };
173+
}) {}
174+
175+
/**
176+
* An example with destructured args
177+
*
178+
* @param options - The options.
179+
* @param options.a - The 'a' string.
180+
* @param options.b - The 'b' string.
181+
*/
182+
export function destructureTest3({ a, b }: { a: string; b: { c: string } }) {}

tests/test_typedoc_analysis/test_typedoc_analysis.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,3 +556,20 @@ def test_code_in_description(self):
556556
557557
And some closing words."""
558558
)
559+
560+
def test_destructured(self):
561+
if TYPEDOC_VERSION < (0, 23, 0):
562+
pytest.xfail("Need typedoc version 0.23 or greater")
563+
obj = self.analyzer.get_object(["destructureTest"])
564+
assert obj.params[0].name == "options.a"
565+
assert obj.params[0].type == "string"
566+
assert obj.params[1].name == "options.b"
567+
assert obj.params[1].type == "{c: string}"
568+
obj = self.analyzer.get_object(["destructureTest2"])
569+
assert obj.params[0].name == "options.a"
570+
assert obj.params[0].type == "string"
571+
assert obj.params[1].name == "options.b"
572+
assert obj.params[1].type == "{c: string}"
573+
obj = self.analyzer.get_object(["destructureTest3"])
574+
assert obj.params[0].name == "options"
575+
assert obj.params[0].type == "{a: string, b: {c: string}}"

0 commit comments

Comments
 (0)