Skip to content

Commit 2eff668

Browse files
authored
Infer type for variable declarations and class properties (#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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 55 additions & 0 deletions
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

Lines changed: 0 additions & 27 deletions
This file was deleted.

test/lib/infer/params.js

Lines changed: 16 additions & 0 deletions
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

Lines changed: 135 additions & 0 deletions
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

Lines changed: 0 additions & 69 deletions
This file was deleted.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy