Skip to content

Commit 2eff668

Browse files
authored
Infer type for variable declarations and class properties (documentationjs#540)
This infers the type from Flow type annotations for variable declarations and class properties. This also moves the logic for setting the `type` for typedefs to the same type inferrer. Also infer the type for statements like: ```js const x = 42; ``` same as: ```js const x: number = 42; ```
1 parent a71bb03 commit 2eff668

File tree

8 files changed

+217
-101
lines changed

8 files changed

+217
-101
lines changed

index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var fs = require('fs'),
2020
inferMembership = require('./lib/infer/membership'),
2121
inferReturn = require('./lib/infer/return'),
2222
inferAccess = require('./lib/infer/access'),
23-
inferTypedefType = require('./lib/infer/typedef_type'),
23+
inferType = require('./lib/infer/type'),
2424
formatLint = require('./lib/lint').formatLint,
2525
garbageCollect = require('./lib/garbage_collect'),
2626
lintComments = require('./lib/lint').lintComments,
@@ -175,7 +175,7 @@ function buildSync(indexes, options) {
175175
inferProperties(),
176176
inferReturn(),
177177
inferMembership(),
178-
inferTypedefType(),
178+
inferType(),
179179
nest,
180180
options.github && github,
181181
garbageCollect);
@@ -245,7 +245,7 @@ function lint(indexes, options, callback) {
245245
inferProperties(),
246246
inferReturn(),
247247
inferMembership(),
248-
inferTypedefType(),
248+
inferType(),
249249
nest);
250250

251251
return expandInputs(indexes, options, function (error, inputs) {

lib/infer/finders.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ function findTarget(path) {
2222
path = path.declaration;
2323
}
2424

25-
// var x = TARGET;
25+
// var x = init;
2626
if (t.isVariableDeclaration(path)) {
27-
return path.declarations[0].init;
27+
return path.declarations[0];
2828
}
2929

3030
// foo.x = TARGET

lib/infer/params.js

+6
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ function inferParams() {
162162
return shouldSkipInference(function inferParams(comment) {
163163
var node = finders.findTarget(comment.context.ast);
164164

165+
// In case of `/** */ var x = function () {}` findTarget returns
166+
// the declarator.
167+
if (t.isVariableDeclarator(node)) {
168+
node = node.init;
169+
}
170+
165171
if (!t.isFunction(node)) {
166172
return comment;
167173
}

lib/infer/type.js

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use strict';
2+
3+
var finders = require('./finders'),
4+
shouldSkipInference = require('./should_skip_inference'),
5+
flowDoctrine = require('../flow_doctrine'),
6+
t = require('babel-types');
7+
8+
var constTypeMapping = {
9+
'BooleanLiteral': {type: 'BooleanTypeAnnotation'},
10+
'NumericLiteral': {type: 'NumberTypeAnnotation'},
11+
'StringLiteral': {type: 'StringTypeAnnotation'}
12+
};
13+
14+
/**
15+
* Infers type tags by using Flow type annotations
16+
*
17+
* @name inferType
18+
* @param {Object} comment parsed comment
19+
* @returns {Object} comment with type tag inferred
20+
*/
21+
module.exports = function () {
22+
return shouldSkipInference(function inferType(comment) {
23+
if (comment.type) {
24+
return comment;
25+
}
26+
27+
var n = finders.findTarget(comment.context.ast);
28+
if (!n) {
29+
return comment;
30+
}
31+
32+
var type;
33+
switch (n.type) {
34+
case 'VariableDeclarator':
35+
type = n.id.typeAnnotation;
36+
if (!type && comment.kind === 'constant') {
37+
type = constTypeMapping[n.init.type];
38+
}
39+
break;
40+
case 'ClassProperty':
41+
type = n.typeAnnotation;
42+
break;
43+
case 'TypeAlias':
44+
type = n.right;
45+
break;
46+
}
47+
if (type) {
48+
if (t.isTypeAnnotation(type)) {
49+
type = type.typeAnnotation;
50+
}
51+
comment.type = flowDoctrine(type);
52+
}
53+
return comment;
54+
});
55+
};

lib/infer/typedef_type.js

-27
This file was deleted.

test/lib/infer/params.js

+16
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,22 @@ test('inferParams', function (t) {
2121
function f(x) {}
2222
}).params, [{lineNumber: 3, name: 'x', title: 'param'}]);
2323

24+
t.deepEqual(evaluate(function () {
25+
/** Test */
26+
var f = function (x) {};
27+
}).params, [{lineNumber: 3, name: 'x', title: 'param'}]);
28+
29+
t.deepEqual(evaluate('/** Test */ var f = (x) => {}').params,
30+
[{lineNumber: 1, name: 'x', title: 'param'}]);
31+
32+
t.deepEqual(evaluate(function () {
33+
var x = 1,
34+
g = function (y) {},
35+
/** Test */
36+
f = function (x) {};
37+
}).params, [{lineNumber: 5, name: 'x', title: 'param'}]);
38+
39+
2440
t.deepEqual(evaluate('/** Test */ export function f(x) {}').params,
2541
[{lineNumber: 1, name: 'x', title: 'param'}]);
2642

test/lib/infer/type.js

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
'use strict';
2+
3+
var test = require('tap').test,
4+
parse = require('../../../lib/parsers/javascript'),
5+
inferKind = require('../../../lib/infer/kind')(),
6+
inferType = require('../../../lib/infer/type')();
7+
8+
function toComment(code) {
9+
return parse({
10+
source: code
11+
})[0];
12+
}
13+
14+
function evaluate(code) {
15+
return inferType(inferKind(toComment(code)));
16+
}
17+
18+
test('inferType', function (t) {
19+
t.deepEqual(evaluate(
20+
'/** @typedef {T} V */'
21+
).type, {
22+
name: 'T',
23+
type: 'NameExpression'
24+
});
25+
26+
t.deepEqual(evaluate(
27+
'/** */' +
28+
'type V = T'
29+
).type, {
30+
name: 'T',
31+
type: 'NameExpression'
32+
});
33+
34+
t.deepEqual(evaluate(
35+
'/** @typedef {Array<T>} V */'
36+
).type, {
37+
applications: [
38+
{
39+
name: 'T',
40+
type: 'NameExpression'
41+
}
42+
],
43+
expression: {
44+
name: 'Array',
45+
type: 'NameExpression'
46+
},
47+
type: 'TypeApplication'
48+
});
49+
50+
t.deepEqual(evaluate(
51+
'/** */' +
52+
'type V = Array<T>'
53+
).type, {
54+
applications: [
55+
{
56+
name: 'T',
57+
type: 'NameExpression'
58+
}
59+
],
60+
expression: {
61+
name: 'Array',
62+
type: 'NameExpression'
63+
},
64+
type: 'TypeApplication'
65+
});
66+
67+
t.deepEqual(evaluate(
68+
'/** */' +
69+
'var x: number'
70+
).type, {
71+
name: 'number',
72+
type: 'NameExpression'
73+
});
74+
75+
t.deepEqual(evaluate(
76+
'/** */' +
77+
'let x: number'
78+
).type, {
79+
name: 'number',
80+
type: 'NameExpression'
81+
});
82+
83+
t.deepEqual(evaluate(
84+
'/** */' +
85+
'const x: number = 42;'
86+
).type, {
87+
name: 'number',
88+
type: 'NameExpression'
89+
});
90+
91+
t.deepEqual(evaluate(
92+
'let x,' +
93+
'/** */' +
94+
'y: number'
95+
).type, {
96+
name: 'number',
97+
type: 'NameExpression'
98+
});
99+
100+
t.deepEqual(evaluate(
101+
'class C {' +
102+
'/** */' +
103+
'x: number;' +
104+
'}'
105+
).type, {
106+
name: 'number',
107+
type: 'NameExpression'
108+
});
109+
110+
t.deepEqual(evaluate(
111+
'/** */' +
112+
'const x = 42;'
113+
).type, {
114+
name: 'number',
115+
type: 'NameExpression'
116+
});
117+
118+
t.deepEqual(evaluate(
119+
'/** */' +
120+
'const x = "abc";'
121+
).type, {
122+
name: 'string',
123+
type: 'NameExpression'
124+
});
125+
126+
t.deepEqual(evaluate(
127+
'/** */' +
128+
'const x = true;'
129+
).type, {
130+
name: 'boolean',
131+
type: 'NameExpression'
132+
});
133+
134+
t.end();
135+
});

test/lib/infer/typedef_type.js

-69
This file was deleted.

0 commit comments

Comments
 (0)