Skip to content

Commit fda2264

Browse files
committed
Handle hooks as function expressions
1 parent ee67ac9 commit fda2264

File tree

5 files changed

+280
-7
lines changed

5 files changed

+280
-7
lines changed

src/parser.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,12 @@ export function parseFromProgram(
165165
}
166166

167167
function visit(node: ts.Node) {
168-
// function x(props: type) { return <div/> }
169168
if (ts.isFunctionDeclaration(node) && node.name) {
170169
if (node.name.getText().startsWith('use')) {
170+
// function useHook(parameters: type): type
171171
parseHook(node);
172172
} else if (node.parameters.length === 1) {
173+
// function x(props: type) { return <div/> }
173174
parseFunctionComponent(node, node);
174175
}
175176
}
@@ -199,11 +200,19 @@ export function parseFromProgram(
199200
parseFunctionComponent(variableNode, node);
200201
}
201202
} else if (
202-
(ts.isArrowFunction(variableNode.initializer) ||
203-
ts.isFunctionExpression(variableNode.initializer)) &&
204-
variableNode.initializer.parameters.length === 1
203+
ts.isArrowFunction(variableNode.initializer) ||
204+
ts.isFunctionExpression(variableNode.initializer)
205205
) {
206-
parseFunctionComponent(variableNode, node);
206+
if (variableNode.name.getText().startsWith('use')) {
207+
// const useHook = function useHook(parameters: type): type
208+
// const useHook = (parameters: type): type
209+
parseHook(variableNode);
210+
} else if (variableNode.initializer.parameters.length === 1) {
211+
// x = (props: type) => { return <div/> }
212+
// x = function(props: type) { return <div/> }
213+
// x = function y(props: type) { return <div/> }
214+
parseFunctionComponent(variableNode, node);
215+
}
207216
}
208217
// x = react.memo((props:type) { return <div/> })
209218
else if (
@@ -285,7 +294,7 @@ export function parseFromProgram(
285294
}
286295
const componentName = node.name.getText();
287296

288-
const type = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration!);
297+
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
289298
type.getCallSignatures().forEach((signature) => {
290299
if (!isTypeJSXElementLike(signature.getReturnType())) {
291300
return;
@@ -411,7 +420,7 @@ export function parseFromProgram(
411420
}
412421
const hookName = node.name.getText();
413422

414-
const type = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration!);
423+
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
415424
const typeStack = new Set<number>([(type as any).id]);
416425

417426
const checkedSignatures = type.getCallSignatures().map((signature) => {

test/hook-arrow-function/input.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* A hook defined as an arrow function.
3+
*
4+
* @internal
5+
*/
6+
export const useHook = (parameters: HookParameters): HookReturnValue => {
7+
return {
8+
getProps(externalProps) {
9+
return externalProps;
10+
},
11+
ref(element: HTMLElement) {},
12+
};
13+
};
14+
15+
interface HookParameters {
16+
value: string;
17+
severity: number;
18+
onChange: (value: string) => void;
19+
}
20+
21+
interface HookReturnValue {
22+
getProps: (externalProps: React.HTMLAttributes<HTMLElement>) => React.HTMLAttributes<HTMLElement>;
23+
ref: React.RefCallback<HTMLElement>;
24+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
{
2+
"nodeType": "program",
3+
"body": [
4+
{
5+
"nodeType": "hook",
6+
"name": "useHook",
7+
"parameters": [
8+
{
9+
"nodeType": "parameter",
10+
"parameterType": {
11+
"nodeType": "prop",
12+
"name": "parameters",
13+
"propType": {
14+
"nodeType": "interface",
15+
"types": [
16+
{
17+
"nodeType": "prop",
18+
"name": "value",
19+
"propType": {
20+
"nodeType": "simpleType",
21+
"typeName": "string"
22+
},
23+
"optional": false,
24+
"filenames": {}
25+
},
26+
{
27+
"nodeType": "prop",
28+
"name": "severity",
29+
"propType": {
30+
"nodeType": "simpleType",
31+
"typeName": "number"
32+
},
33+
"optional": false,
34+
"filenames": {}
35+
},
36+
{
37+
"nodeType": "prop",
38+
"name": "onChange",
39+
"propType": {
40+
"nodeType": "function",
41+
"parameters": [
42+
{
43+
"nodeType": "parameter",
44+
"parameterType": {
45+
"nodeType": "prop",
46+
"name": "value",
47+
"propType": {
48+
"nodeType": "simpleType",
49+
"typeName": "string"
50+
},
51+
"optional": false,
52+
"filenames": {}
53+
}
54+
}
55+
],
56+
"returnValue": {
57+
"nodeType": "simpleType",
58+
"typeName": "void"
59+
}
60+
},
61+
"optional": false,
62+
"filenames": {}
63+
}
64+
]
65+
},
66+
"optional": false,
67+
"filenames": {}
68+
}
69+
}
70+
],
71+
"returnValue": {
72+
"nodeType": "interface",
73+
"types": [
74+
{
75+
"nodeType": "prop",
76+
"name": "getProps",
77+
"propType": {
78+
"nodeType": "function",
79+
"parameters": [
80+
{
81+
"nodeType": "parameter",
82+
"parameterType": {
83+
"nodeType": "prop",
84+
"name": "externalProps",
85+
"propType": {
86+
"nodeType": "simpleType",
87+
"typeName": "React.HTMLAttributes"
88+
},
89+
"optional": false,
90+
"filenames": {}
91+
}
92+
}
93+
],
94+
"returnValue": {
95+
"nodeType": "simpleType",
96+
"typeName": "React.HTMLAttributes"
97+
}
98+
},
99+
"optional": false,
100+
"filenames": {}
101+
}
102+
]
103+
},
104+
"description": "A hook defined as an arrow function.",
105+
"visibility": "internal"
106+
}
107+
]
108+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* A hook defined as a function expression.
3+
*
4+
* @internal
5+
*/
6+
export const useHook = function useHook(parameters: HookParameters): HookReturnValue {
7+
return {
8+
getProps(externalProps) {
9+
return externalProps;
10+
},
11+
ref(element: HTMLElement) {},
12+
};
13+
};
14+
15+
interface HookParameters {
16+
value: string;
17+
severity: number;
18+
onChange: (value: string) => void;
19+
}
20+
21+
interface HookReturnValue {
22+
getProps: (externalProps: React.HTMLAttributes<HTMLElement>) => React.HTMLAttributes<HTMLElement>;
23+
ref: React.RefCallback<HTMLElement>;
24+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
{
2+
"nodeType": "program",
3+
"body": [
4+
{
5+
"nodeType": "hook",
6+
"name": "useHook",
7+
"parameters": [
8+
{
9+
"nodeType": "parameter",
10+
"parameterType": {
11+
"nodeType": "prop",
12+
"name": "parameters",
13+
"propType": {
14+
"nodeType": "interface",
15+
"types": [
16+
{
17+
"nodeType": "prop",
18+
"name": "value",
19+
"propType": {
20+
"nodeType": "simpleType",
21+
"typeName": "string"
22+
},
23+
"optional": false,
24+
"filenames": {}
25+
},
26+
{
27+
"nodeType": "prop",
28+
"name": "severity",
29+
"propType": {
30+
"nodeType": "simpleType",
31+
"typeName": "number"
32+
},
33+
"optional": false,
34+
"filenames": {}
35+
},
36+
{
37+
"nodeType": "prop",
38+
"name": "onChange",
39+
"propType": {
40+
"nodeType": "function",
41+
"parameters": [
42+
{
43+
"nodeType": "parameter",
44+
"parameterType": {
45+
"nodeType": "prop",
46+
"name": "value",
47+
"propType": {
48+
"nodeType": "simpleType",
49+
"typeName": "string"
50+
},
51+
"optional": false,
52+
"filenames": {}
53+
}
54+
}
55+
],
56+
"returnValue": {
57+
"nodeType": "simpleType",
58+
"typeName": "void"
59+
}
60+
},
61+
"optional": false,
62+
"filenames": {}
63+
}
64+
]
65+
},
66+
"optional": false,
67+
"filenames": {}
68+
}
69+
}
70+
],
71+
"returnValue": {
72+
"nodeType": "interface",
73+
"types": [
74+
{
75+
"nodeType": "prop",
76+
"name": "getProps",
77+
"propType": {
78+
"nodeType": "function",
79+
"parameters": [
80+
{
81+
"nodeType": "parameter",
82+
"parameterType": {
83+
"nodeType": "prop",
84+
"name": "externalProps",
85+
"propType": {
86+
"nodeType": "simpleType",
87+
"typeName": "React.HTMLAttributes"
88+
},
89+
"optional": false,
90+
"filenames": {}
91+
}
92+
}
93+
],
94+
"returnValue": {
95+
"nodeType": "simpleType",
96+
"typeName": "React.HTMLAttributes"
97+
}
98+
},
99+
"optional": false,
100+
"filenames": {}
101+
}
102+
]
103+
},
104+
"description": "A hook defined as a function expression.",
105+
"visibility": "internal"
106+
}
107+
]
108+
}

0 commit comments

Comments
 (0)