1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | import * as doctrine from 'doctrine';
|
19 |
|
20 | import * as ts from './ts-ast';
|
21 |
|
22 | const {parseType, parseParamType} = require('doctrine/lib/typed.js');
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | export function closureTypeToTypeScript(
|
29 | closureType: (string|null|undefined),
|
30 | templateTypes: string[] = []): ts.Type {
|
31 | if (!closureType) {
|
32 | return ts.anyType;
|
33 | }
|
34 | let ast;
|
35 | try {
|
36 | ast = parseType(closureType);
|
37 | } catch {
|
38 | return ts.anyType;
|
39 | }
|
40 | return convert(ast, templateTypes);
|
41 | }
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | export function closureParamToTypeScript(
|
49 | closureType: (string|null|undefined),
|
50 | templateTypes: string[] = [],
|
51 | ): {type: ts.Type, optional: boolean, rest: boolean} {
|
52 | if (!closureType) {
|
53 | return {type: ts.anyType, optional: false, rest: false};
|
54 | }
|
55 |
|
56 | let ast;
|
57 | try {
|
58 | ast = parseParamType(closureType);
|
59 | } catch {
|
60 | return {
|
61 | type: ts.anyType,
|
62 |
|
63 |
|
64 |
|
65 | optional: closureType.endsWith('='),
|
66 | rest: false,
|
67 | };
|
68 | }
|
69 |
|
70 |
|
71 | switch (ast.type) {
|
72 | case 'OptionalType':
|
73 | return {
|
74 | type: convert(ast.expression, templateTypes),
|
75 | optional: true,
|
76 | rest: false,
|
77 | };
|
78 | case 'RestType':
|
79 | return {
|
80 |
|
81 |
|
82 |
|
83 | type: new ts.ArrayType(convert(ast.expression, templateTypes)),
|
84 | optional: false,
|
85 | rest: true,
|
86 | };
|
87 | default:
|
88 | return {
|
89 | type: convert(ast, templateTypes),
|
90 | optional: false,
|
91 | rest: false,
|
92 | };
|
93 | }
|
94 | }
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 | function convert(node: doctrine.Type, templateTypes: string[]): ts.Type {
|
101 | let nullable;
|
102 | if (isNullable(node)) {
|
103 | nullable = true;
|
104 | node = node.expression;
|
105 | } else if (isNonNullable(node)) {
|
106 | nullable = false;
|
107 | node = node.expression;
|
108 | } else if (isName(node) && templateTypes.includes(node.name)) {
|
109 |
|
110 |
|
111 |
|
112 | nullable = false;
|
113 | } else {
|
114 | nullable = nullableByDefault(node);
|
115 | }
|
116 |
|
117 | let t: ts.Type;
|
118 |
|
119 | if (isParameterizedArray(node)) {
|
120 | t = convertArray(node, templateTypes);
|
121 | } else if (isUnion(node)) {
|
122 | t = convertUnion(node, templateTypes);
|
123 | } else if (isFunction(node)) {
|
124 | t = convertFunction(node, templateTypes);
|
125 | } else if (isBareArray(node)) {
|
126 | t = new ts.ArrayType(ts.anyType);
|
127 | } else if (isRecordType(node)) {
|
128 | t = convertRecord(node, templateTypes);
|
129 | } else if (isAllLiteral(node)) {
|
130 | t = ts.anyType;
|
131 | } else if (isNullableLiteral(node)) {
|
132 | t = ts.anyType;
|
133 | } else if (isNullLiteral(node)) {
|
134 | t = ts.nullType;
|
135 | } else if (isUndefinedLiteral(node)) {
|
136 | t = ts.undefinedType;
|
137 | } else if (isVoidLiteral(node)) {
|
138 | t = new ts.NameType('void');
|
139 | } else if (isName(node)) {
|
140 | if (node.name === 'Object') {
|
141 |
|
142 |
|
143 |
|
144 | t = new ts.NameType('object');
|
145 | } else {
|
146 | t = new ts.NameType(node.name);
|
147 | }
|
148 | } else {
|
149 | console.error('Unknown syntax.');
|
150 | return ts.anyType;
|
151 | }
|
152 |
|
153 | if (nullable) {
|
154 | t = new ts.UnionType([t, ts.nullType]);
|
155 | }
|
156 |
|
157 | return t;
|
158 | }
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | function nullableByDefault(node: doctrine.Type): boolean {
|
165 | if (isName(node)) {
|
166 | switch (node.name) {
|
167 | case 'string':
|
168 | case 'number':
|
169 | case 'boolean':
|
170 | case 'void':
|
171 | return false
|
172 | }
|
173 | return true;
|
174 | }
|
175 | return isParameterizedArray(node);
|
176 | }
|
177 |
|
178 | function convertArray(
|
179 | node: doctrine.type.TypeApplication, templateTypes: string[]): ts.Type {
|
180 | const applications = node.applications;
|
181 | return new ts.ArrayType(
|
182 | applications.length === 1 ? convert(applications[0], templateTypes) :
|
183 | ts.anyType);
|
184 | }
|
185 |
|
186 | function convertUnion(
|
187 | node: doctrine.type.UnionType, templateTypes: string[]): ts.Type {
|
188 | return new ts.UnionType(
|
189 | node.elements.map((element) => convert(element, templateTypes)));
|
190 | }
|
191 |
|
192 | function convertFunction(
|
193 | node: doctrine.type.FunctionType,
|
194 | templateTypes: string[]): ts.FunctionType|ts.ConstructorType {
|
195 | const params = node.params.map(
|
196 | (param, idx) => new ts.ParamType(
|
197 |
|
198 | 'p' + idx,
|
199 | convert(param, templateTypes)));
|
200 | if (node.new) {
|
201 | return new ts.ConstructorType(
|
202 | params,
|
203 |
|
204 |
|
205 |
|
206 | isName(node.this) ? new ts.NameType(node.this.name) : ts.anyType);
|
207 | } else {
|
208 | return new ts.FunctionType(
|
209 | params,
|
210 |
|
211 | node.result ? convert(node.result as any, templateTypes) : ts.anyType);
|
212 | }
|
213 | }
|
214 |
|
215 | function convertRecord(node: doctrine.type.RecordType, templateTypes: string[]):
|
216 | ts.RecordType|ts.NameType {
|
217 | const fields = [];
|
218 | for (const field of node.fields) {
|
219 | if (field.type !== 'FieldType') {
|
220 | return ts.anyType;
|
221 | }
|
222 | let fieldType =
|
223 | field.value ? convert(field.value, templateTypes) : ts.anyType;
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 | let optional = false;
|
231 | if (fieldType.kind === 'union') {
|
232 | fieldType.members = fieldType.members.filter((member) => {
|
233 | if (member.kind === 'name' && member.name === 'undefined') {
|
234 | optional = true;
|
235 | return false;
|
236 | }
|
237 | return true;
|
238 | });
|
239 |
|
240 |
|
241 | fieldType.simplify();
|
242 | }
|
243 |
|
244 | fields.push(new ts.ParamType(field.key, fieldType, optional));
|
245 | }
|
246 | return new ts.RecordType(fields);
|
247 | }
|
248 |
|
249 | function isParameterizedArray(node: doctrine.Type):
|
250 | node is doctrine.type.TypeApplication {
|
251 | return node.type === 'TypeApplication' &&
|
252 | node.expression.type === 'NameExpression' &&
|
253 | node.expression.name === 'Array';
|
254 | }
|
255 |
|
256 | function isBareArray(node: doctrine.Type):
|
257 | node is doctrine.type.TypeApplication {
|
258 | return node.type === 'NameExpression' && node.name === 'Array';
|
259 | }
|
260 |
|
261 | function isUnion(node: doctrine.Type): node is doctrine.type.UnionType {
|
262 | return node.type === 'UnionType';
|
263 | }
|
264 |
|
265 | function isFunction(node: doctrine.Type): node is doctrine.type.FunctionType {
|
266 | return node.type === 'FunctionType';
|
267 | }
|
268 |
|
269 | function isRecordType(node: doctrine.Type): node is doctrine.type.RecordType {
|
270 | return node.type === 'RecordType';
|
271 | }
|
272 |
|
273 | function isNullable(node: doctrine.Type): node is doctrine.type.NullableType {
|
274 | return node.type === 'NullableType';
|
275 | }
|
276 |
|
277 | function isNonNullable(node: doctrine.Type):
|
278 | node is doctrine.type.NonNullableType {
|
279 | return node.type === 'NonNullableType';
|
280 | }
|
281 |
|
282 | function isAllLiteral(node: doctrine.Type): node is doctrine.type.AllLiteral {
|
283 | return node.type === 'AllLiteral';
|
284 | }
|
285 |
|
286 | function isNullLiteral(node: doctrine.Type): node is doctrine.type.NullLiteral {
|
287 | return node.type === 'NullLiteral';
|
288 | }
|
289 |
|
290 | function isNullableLiteral(node: doctrine.Type):
|
291 | node is doctrine.type.NullableLiteral {
|
292 | return node.type === 'NullableLiteral';
|
293 | }
|
294 |
|
295 | function isUndefinedLiteral(node: doctrine.Type):
|
296 | node is doctrine.type.UndefinedLiteral {
|
297 | return node.type === 'UndefinedLiteral';
|
298 | }
|
299 |
|
300 | function isVoidLiteral(node: doctrine.Type): node is doctrine.type.VoidLiteral {
|
301 | return node.type === 'VoidLiteral';
|
302 | }
|
303 |
|
304 | function isName(node: doctrine.Type): node is doctrine.type.NameExpression {
|
305 | return node.type === 'NameExpression';
|
306 | }
|