documentation/lib/flow_doctrine.js
Erik Arvidsson 16da8bb225 Infer optional record fields in object types (#547)
This is done with Doctrine's OptionalType, which Doctrine is only
using for parameter types as of now.

Towards #512
2016-09-19 12:54:56 -04:00

147 lines
3.3 KiB
JavaScript

'use strict';
var namedTypes = {
'NumberTypeAnnotation': 'number',
'BooleanTypeAnnotation': 'boolean',
'StringTypeAnnotation': 'string'
};
var oneToOne = {
'AnyTypeAnnotation': 'AllLiteral',
'MixedTypeAnnotation': 'AllLiteral',
'NullLiteralTypeAnnotation': 'NullLiteral',
'VoidTypeAnnotation': 'VoidLiteral'
};
var literalTypes = {
'BooleanLiteralTypeAnnotation': 'BooleanLiteralType',
'NumericLiteralTypeAnnotation': 'NumericLiteralType',
'StringLiteralTypeAnnotation': 'StringLiteralType'
};
function propertyToField(property) {
var type = flowDoctrine(property.value);
if (property.optional) {
// Doctrine does not support optional fields but it does have something called optional types
// (which makes no sense, but let's play along).
type = {
type: 'OptionalType',
expression: type
};
}
return {
type: 'FieldType',
key: property.key.name,
value: type
};
}
/**
* Babel parses Flow annotations in JavaScript into AST nodes. documentation.js uses
* Babel to parse JavaScript. This method restructures those Babel-generated
* objects into objects that fit the output of Doctrine, the module we use
* to parse JSDoc annotations. This lets us use Flow annotations _as_
* JSDoc annotations.
*
* @private
* @param {Object} type babel-parsed flow type
* @returns {Object} doctrine compatible type
*/
function flowDoctrine(type) {
if (type.type in namedTypes) {
return {
type: 'NameExpression',
name: namedTypes[type.type]
};
}
if (type.type in oneToOne) {
return {type: oneToOne[type.type]};
}
switch (type.type) {
case 'NullableTypeAnnotation':
return {
type: 'NullableType',
expression: flowDoctrine(type.typeAnnotation)
};
case 'UnionTypeAnnotation':
return {
type: 'UnionType',
elements: type.types.map(flowDoctrine)
};
// [number]
// [string, boolean, number]
case 'TupleTypeAnnotation':
return {
type: 'ArrayType',
elements: type.types.map(flowDoctrine)
};
// number[]
case 'ArrayTypeAnnotation':
return {
type: 'TypeApplication',
expression: {
type: 'NameExpression',
name: 'Array'
},
applications: [flowDoctrine(type.elementType)]
};
// (y: number) => bool
case 'FunctionTypeAnnotation':
return {
type: 'FunctionType',
params: type.params.map(function (param) {
return {
type: 'ParameterType',
name: param.name.name,
expression: flowDoctrine(param.typeAnnotation)
};
}),
result: flowDoctrine(type.returnType)
};
case 'GenericTypeAnnotation':
if (type.typeParameters) {
return {
type: 'TypeApplication',
expression: {
type: 'NameExpression',
name: type.id.name
},
applications: type.typeParameters.params.map(flowDoctrine)
};
}
return {
type: 'NameExpression',
name: type.id.name
};
case 'ObjectTypeAnnotation':
if (type.properties) {
return {
type: 'RecordType',
fields: type.properties.map(propertyToField)
};
}
return {
type: 'NameExpression',
name: type.id.name
};
}
if (type.type in literalTypes) {
return {
type: literalTypes[type.type],
value: type.value
};
}
}
module.exports = flowDoctrine;