mirror of
https://github.com/documentationjs/documentation.git
synced 2026-01-25 14:26:29 +00:00
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; ```
This commit is contained in:
parent
a71bb03681
commit
2eff668edf
6
index.js
6
index.js
@ -20,7 +20,7 @@ var fs = require('fs'),
|
||||
inferMembership = require('./lib/infer/membership'),
|
||||
inferReturn = require('./lib/infer/return'),
|
||||
inferAccess = require('./lib/infer/access'),
|
||||
inferTypedefType = require('./lib/infer/typedef_type'),
|
||||
inferType = require('./lib/infer/type'),
|
||||
formatLint = require('./lib/lint').formatLint,
|
||||
garbageCollect = require('./lib/garbage_collect'),
|
||||
lintComments = require('./lib/lint').lintComments,
|
||||
@ -175,7 +175,7 @@ function buildSync(indexes, options) {
|
||||
inferProperties(),
|
||||
inferReturn(),
|
||||
inferMembership(),
|
||||
inferTypedefType(),
|
||||
inferType(),
|
||||
nest,
|
||||
options.github && github,
|
||||
garbageCollect);
|
||||
@ -245,7 +245,7 @@ function lint(indexes, options, callback) {
|
||||
inferProperties(),
|
||||
inferReturn(),
|
||||
inferMembership(),
|
||||
inferTypedefType(),
|
||||
inferType(),
|
||||
nest);
|
||||
|
||||
return expandInputs(indexes, options, function (error, inputs) {
|
||||
|
||||
@ -22,9 +22,9 @@ function findTarget(path) {
|
||||
path = path.declaration;
|
||||
}
|
||||
|
||||
// var x = TARGET;
|
||||
// var x = init;
|
||||
if (t.isVariableDeclaration(path)) {
|
||||
return path.declarations[0].init;
|
||||
return path.declarations[0];
|
||||
}
|
||||
|
||||
// foo.x = TARGET
|
||||
|
||||
@ -162,6 +162,12 @@ function inferParams() {
|
||||
return shouldSkipInference(function inferParams(comment) {
|
||||
var node = finders.findTarget(comment.context.ast);
|
||||
|
||||
// In case of `/** */ var x = function () {}` findTarget returns
|
||||
// the declarator.
|
||||
if (t.isVariableDeclarator(node)) {
|
||||
node = node.init;
|
||||
}
|
||||
|
||||
if (!t.isFunction(node)) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
55
lib/infer/type.js
Normal file
55
lib/infer/type.js
Normal file
@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
var finders = require('./finders'),
|
||||
shouldSkipInference = require('./should_skip_inference'),
|
||||
flowDoctrine = require('../flow_doctrine'),
|
||||
t = require('babel-types');
|
||||
|
||||
var constTypeMapping = {
|
||||
'BooleanLiteral': {type: 'BooleanTypeAnnotation'},
|
||||
'NumericLiteral': {type: 'NumberTypeAnnotation'},
|
||||
'StringLiteral': {type: 'StringTypeAnnotation'}
|
||||
};
|
||||
|
||||
/**
|
||||
* Infers type tags by using Flow type annotations
|
||||
*
|
||||
* @name inferType
|
||||
* @param {Object} comment parsed comment
|
||||
* @returns {Object} comment with type tag inferred
|
||||
*/
|
||||
module.exports = function () {
|
||||
return shouldSkipInference(function inferType(comment) {
|
||||
if (comment.type) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
var n = finders.findTarget(comment.context.ast);
|
||||
if (!n) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
var type;
|
||||
switch (n.type) {
|
||||
case 'VariableDeclarator':
|
||||
type = n.id.typeAnnotation;
|
||||
if (!type && comment.kind === 'constant') {
|
||||
type = constTypeMapping[n.init.type];
|
||||
}
|
||||
break;
|
||||
case 'ClassProperty':
|
||||
type = n.typeAnnotation;
|
||||
break;
|
||||
case 'TypeAlias':
|
||||
type = n.right;
|
||||
break;
|
||||
}
|
||||
if (type) {
|
||||
if (t.isTypeAnnotation(type)) {
|
||||
type = type.typeAnnotation;
|
||||
}
|
||||
comment.type = flowDoctrine(type);
|
||||
}
|
||||
return comment;
|
||||
});
|
||||
};
|
||||
@ -1,27 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var shouldSkipInference = require('./should_skip_inference'),
|
||||
flowDoctrine = require('../flow_doctrine'),
|
||||
finders = require('./finders');
|
||||
|
||||
/**
|
||||
* Infers the type of typedefs defined by Flow type aliases
|
||||
*
|
||||
* @name inferTypedefType
|
||||
* @param {Object} comment parsed comment
|
||||
* @returns {Object} comment with type tag inferred
|
||||
*/
|
||||
module.exports = function () {
|
||||
return shouldSkipInference(function inferTypedefType(comment) {
|
||||
if (comment.kind !== 'typedef') {
|
||||
return comment;
|
||||
}
|
||||
|
||||
var flowAlias = finders.findTarget(comment.context.ast);
|
||||
if (flowAlias && flowAlias.type === 'TypeAlias') {
|
||||
comment.type = flowDoctrine(flowAlias.right);
|
||||
}
|
||||
|
||||
return comment;
|
||||
});
|
||||
};
|
||||
@ -21,6 +21,22 @@ test('inferParams', function (t) {
|
||||
function f(x) {}
|
||||
}).params, [{lineNumber: 3, name: 'x', title: 'param'}]);
|
||||
|
||||
t.deepEqual(evaluate(function () {
|
||||
/** Test */
|
||||
var f = function (x) {};
|
||||
}).params, [{lineNumber: 3, name: 'x', title: 'param'}]);
|
||||
|
||||
t.deepEqual(evaluate('/** Test */ var f = (x) => {}').params,
|
||||
[{lineNumber: 1, name: 'x', title: 'param'}]);
|
||||
|
||||
t.deepEqual(evaluate(function () {
|
||||
var x = 1,
|
||||
g = function (y) {},
|
||||
/** Test */
|
||||
f = function (x) {};
|
||||
}).params, [{lineNumber: 5, name: 'x', title: 'param'}]);
|
||||
|
||||
|
||||
t.deepEqual(evaluate('/** Test */ export function f(x) {}').params,
|
||||
[{lineNumber: 1, name: 'x', title: 'param'}]);
|
||||
|
||||
|
||||
135
test/lib/infer/type.js
Normal file
135
test/lib/infer/type.js
Normal file
@ -0,0 +1,135 @@
|
||||
'use strict';
|
||||
|
||||
var test = require('tap').test,
|
||||
parse = require('../../../lib/parsers/javascript'),
|
||||
inferKind = require('../../../lib/infer/kind')(),
|
||||
inferType = require('../../../lib/infer/type')();
|
||||
|
||||
function toComment(code) {
|
||||
return parse({
|
||||
source: code
|
||||
})[0];
|
||||
}
|
||||
|
||||
function evaluate(code) {
|
||||
return inferType(inferKind(toComment(code)));
|
||||
}
|
||||
|
||||
test('inferType', function (t) {
|
||||
t.deepEqual(evaluate(
|
||||
'/** @typedef {T} V */'
|
||||
).type, {
|
||||
name: 'T',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'type V = T'
|
||||
).type, {
|
||||
name: 'T',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** @typedef {Array<T>} V */'
|
||||
).type, {
|
||||
applications: [
|
||||
{
|
||||
name: 'T',
|
||||
type: 'NameExpression'
|
||||
}
|
||||
],
|
||||
expression: {
|
||||
name: 'Array',
|
||||
type: 'NameExpression'
|
||||
},
|
||||
type: 'TypeApplication'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'type V = Array<T>'
|
||||
).type, {
|
||||
applications: [
|
||||
{
|
||||
name: 'T',
|
||||
type: 'NameExpression'
|
||||
}
|
||||
],
|
||||
expression: {
|
||||
name: 'Array',
|
||||
type: 'NameExpression'
|
||||
},
|
||||
type: 'TypeApplication'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'var x: number'
|
||||
).type, {
|
||||
name: 'number',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'let x: number'
|
||||
).type, {
|
||||
name: 'number',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'const x: number = 42;'
|
||||
).type, {
|
||||
name: 'number',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'let x,' +
|
||||
'/** */' +
|
||||
'y: number'
|
||||
).type, {
|
||||
name: 'number',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'class C {' +
|
||||
'/** */' +
|
||||
'x: number;' +
|
||||
'}'
|
||||
).type, {
|
||||
name: 'number',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'const x = 42;'
|
||||
).type, {
|
||||
name: 'number',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'const x = "abc";'
|
||||
).type, {
|
||||
name: 'string',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'const x = true;'
|
||||
).type, {
|
||||
name: 'boolean',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
@ -1,69 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var test = require('tap').test,
|
||||
parse = require('../../../lib/parsers/javascript'),
|
||||
inferKind = require('../../../lib/infer/kind')(),
|
||||
inferTypedefType = require('../../../lib/infer/typedef_type')();
|
||||
|
||||
function toComment(code) {
|
||||
return parse({
|
||||
source: code
|
||||
})[0];
|
||||
}
|
||||
|
||||
function evaluate(code) {
|
||||
return inferTypedefType(inferKind(toComment(code)));
|
||||
}
|
||||
|
||||
test('inferTypedefType', function (t) {
|
||||
t.deepEqual(evaluate(
|
||||
'/** @typedef {T} V */'
|
||||
).type, {
|
||||
name: 'T',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'type V = T'
|
||||
).type, {
|
||||
name: 'T',
|
||||
type: 'NameExpression'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** @typedef {Array<T>} V */'
|
||||
).type, {
|
||||
applications: [
|
||||
{
|
||||
name: 'T',
|
||||
type: 'NameExpression'
|
||||
}
|
||||
],
|
||||
expression: {
|
||||
name: 'Array',
|
||||
type: 'NameExpression'
|
||||
},
|
||||
type: 'TypeApplication'
|
||||
});
|
||||
|
||||
t.deepEqual(evaluate(
|
||||
'/** */' +
|
||||
'type V = Array<T>'
|
||||
).type, {
|
||||
applications: [
|
||||
{
|
||||
name: 'T',
|
||||
type: 'NameExpression'
|
||||
}
|
||||
],
|
||||
expression: {
|
||||
name: 'Array',
|
||||
type: 'NameExpression'
|
||||
},
|
||||
type: 'TypeApplication'
|
||||
});
|
||||
|
||||
|
||||
t.end();
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user