Skip to content

Commit 80623a2

Browse files
authored
Merge pull request #42 from wehrstedt/issue/41
Fixed mapping of multi dimensional arrays
2 parents 5dd88af + fcd7922 commit 80623a2

10 files changed

+441
-40
lines changed

src/core/jsdoc-tsd-parser.ts

+47-24
Original file line numberDiff line numberDiff line change
@@ -675,35 +675,58 @@ export class JSDocTsdParser {
675675
}
676676

677677
private mapVariableType(variableType: string) {
678-
let matches = variableType.match(/(?:Array\.<([^>]+)>)|(?:([^\[]*)\[\])/i);
679-
680-
if (matches) {
681-
let type = matches[1] || matches[2];
682-
683-
if (type === "*" || type === "") {
684-
// wrong type definition
685-
return dom.type.any;
678+
// resolve array types
679+
// jsdoc will provide arrays always as "Array.<>" if it's typed or as "Array" if it's not typed
680+
let resultType: dom.Type = dom.type.any;
681+
while (/^Array/i.test(variableType)) {
682+
// it's an array, check if it's typed
683+
let arrayTypeMatches = variableType.match(/Array\.<(\(?[\w|]+\)?)>/i); // @todo: can contain namepaths
684+
if (arrayTypeMatches && !!arrayTypeMatches[1]) {
685+
const arrayTypeString: string = arrayTypeMatches[1];
686+
const arrayType = (arrayTypeString.toLowerCase() === "array") ? dom.type.array(dom.type.any) : this.mapVariableTypeString(arrayTypeString)
687+
resultType = (resultType === dom.type.any)
688+
? dom.type.array(arrayType)
689+
: dom.type.array(resultType); // nested array
690+
691+
// remove the string from the variable type (nested arrays)
692+
const regExp = new RegExp(`Array.<${arrayTypeString.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}>`, "i");
693+
variableType = variableType.replace(regExp, "");
686694
} else {
687-
if (type === "bool") {
688-
type = "boolean";
689-
}
695+
resultType = dom.type.array(resultType);
690696

691-
return dom.type.array(type as dom.Type);
697+
// remove the array keyword
698+
variableType = variableType.replace(/^Array(\.<)?/i, "");
692699
}
693-
} else {
694-
if (variableType.match(/array/i) || variableType === "*") {
695-
return dom.type.any;
696-
} else {
697-
if (variableType === "bool") {
698-
variableType = "boolean";
699-
}
700+
}
700701

701-
if (variableType === "function") {
702-
variableType = "Function";
703-
}
704-
return variableType as dom.Type;
705-
}
702+
if (resultType === dom.type.any) {
703+
resultType = this.mapVariableTypeString(variableType);
704+
}
705+
706+
return resultType;
707+
}
708+
709+
private mapVariableTypeString(variableType: string): dom.Type {
710+
if (variableType === "bool") {
711+
variableType = "boolean";
712+
}
713+
714+
if (variableType === "function") {
715+
variableType = "Function";
706716
}
717+
718+
if (variableType === "*") {
719+
variableType = "any";
720+
}
721+
722+
// check if it's a union type
723+
let resultType: dom.Type = variableType as dom.Type;
724+
if (variableType.indexOf("|") > -1) {
725+
variableType = variableType.replace(/\(|\)/g, "");
726+
resultType = this.mapTypesToUnion(variableType.split("|"));
727+
}
728+
729+
return resultType;
707730
}
708731

709732
private parseClass(jsdocItem: IClassDoclet, domClass?: dom.ClassDeclaration): dom.DeclarationBase {

src/test/function/test.jsdoc-tsd-parser.ts

+12-16
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,17 @@ describe("JSDocTsdParser.parse.function", () => {
222222
expect(functionData.returns[0].type.names.length).to.equal(1);
223223

224224
// add different array parameters to the function
225+
// be sure that the types passed below are a valid return value of jsdoc, e.g. JSDoc transforms string[] to Array.<string>
225226
functionData.params = [
226227
{
227228
name: "param1",
228229
type: {
229230
names: [
230231
"Array.<string>",
231232
"array.<boolean>",
232-
"object[]",
233+
"Array.<object>",
233234
"array",
234235
"Array",
235-
"[]",
236-
"*[]",
237236
"Array.<*>",
238237
"array.<*>"
239238
]
@@ -252,20 +251,20 @@ describe("JSDocTsdParser.parse.function", () => {
252251

253252
// ensure that every type is mapped correctly
254253
let union = functionDeclarations[0].parameters[0].type as dom.UnionType;
255-
expect(union.members.length).to.eq(9);
254+
expect(union.members.length).to.eq(7);
256255

257256
expect((union.members[0] as any).kind).to.eq("array");
258257
expect((union.members[0] as any).type).to.eq(dom.type.string);
259258
expect((union.members[1] as any).kind).to.eq("array");
260259
expect((union.members[1] as any).type).to.eq(dom.type.boolean);
261260
expect((union.members[2] as any).kind).to.eq("array");
262261
expect((union.members[2] as any).type).to.eq(dom.type.object);
263-
expect(union.members[3]).to.eq(dom.type.any);
264-
expect(union.members[4]).to.eq(dom.type.any);
265-
expect(union.members[5]).to.eq(dom.type.any);
266-
expect(union.members[6]).to.eq(dom.type.any);
267-
expect(union.members[7]).to.eq(dom.type.any);
268-
expect(union.members[8]).to.eq(dom.type.any);
262+
263+
const anyArray = dom.type.array(dom.type.any);
264+
expect(union.members[3]).to.deep.eq(anyArray);
265+
expect(union.members[4]).to.deep.eq(anyArray);
266+
expect(union.members[5]).to.deep.eq(anyArray);
267+
expect(union.members[6]).to.deep.eq(anyArray);
269268

270269
});
271270

@@ -287,8 +286,8 @@ describe("JSDocTsdParser.parse.function", () => {
287286
names: [
288287
"bool",
289288
"boolean",
290-
"bool[]",
291-
"boolean[]"
289+
"Array.<bool>",
290+
"Array.<boolean>"
292291
]
293292
},
294293
comment: "..",
@@ -360,7 +359,6 @@ describe("JSDocTsdParser.parse.function", () => {
360359
description: "..",
361360
type: {
362361
names: [
363-
primitiveTypeValue + "[]",
364362
"Array.<" + primitiveTypeValue + ">"
365363
]
366364
}
@@ -374,11 +372,9 @@ describe("JSDocTsdParser.parse.function", () => {
374372
expect(functionDeclarations.length).to.eq(1);
375373

376374
let union = functionDeclarations[0].returnType as dom.UnionType;
377-
expect(union.members.length).to.eq(2);
375+
expect(union.members.length).to.eq(1);
378376
expect((union.members[0] as any).kind).to.eq("array");
379377
expect((union.members[0] as any).type).to.eq(primitiveTypeValue);
380-
expect((union.members[1] as any).kind).to.eq("array");
381-
expect((union.members[1] as any).type).to.eq(primitiveTypeValue);
382378
}
383379
});
384380

src/test/test.example.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { expect } from "chai";
22
import * as dom from "dts-dom";
33
import * as fs from "fs";
44
import * as path from "path";
5+
import { execSync } from "child_process";
56
import { JSDocTsdParser } from "../core/jsdoc-tsd-parser";
67

78
describe("JSDocTsdParser.parse.exampleProject", () => {
89
it("Should parse the example project", () => {
10+
var a = execSync("npm run parse-example");
911
let data: TDoclet[] = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../../exampleProject/jsdoc-results.json"), { encoding: "utf-8" }));
1012

1113
let parser = new JSDocTsdParser();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"comment": "/**\r\n * @param {string[][][][]} [field] ...\r\n */",
4+
"meta": {
5+
"range": [
6+
50,
7+
79
8+
],
9+
"filename": "test.js",
10+
"lineno": 4,
11+
"columnno": 0,
12+
"path": "e:\\jsdoc-tsd\\exampleProject\\src",
13+
"code": {
14+
"id": "astnode100000002",
15+
"name": "myFunction",
16+
"type": "FunctionDeclaration",
17+
"paramnames": [
18+
"field"
19+
]
20+
}
21+
},
22+
"params": [
23+
{
24+
"type": {
25+
"names": [
26+
"Array.<Array.<Array.<Array.<string>>>>"
27+
]
28+
},
29+
"optional": true,
30+
"description": "...",
31+
"name": "field"
32+
}
33+
],
34+
"name": "myFunction",
35+
"longname": "myFunction",
36+
"kind": "function",
37+
"scope": "global",
38+
"___id": "T000002R000002",
39+
"___s": true
40+
},
41+
{
42+
"kind": "package",
43+
"longname": "package:undefined",
44+
"files": [
45+
"e:\\jsdoc-tsd\\exampleProject\\src\\test.js"
46+
],
47+
"___id": "T000002R000003",
48+
"___s": true
49+
}
50+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"comment": "/**\r\n * @param {string[]} [field] ...\r\n */",
4+
"meta": {
5+
"range": [
6+
44,
7+
73
8+
],
9+
"filename": "test.js",
10+
"lineno": 4,
11+
"columnno": 0,
12+
"path": "e:\\jsdoc-tsd\\exampleProject\\src",
13+
"code": {
14+
"id": "astnode100000002",
15+
"name": "myFunction",
16+
"type": "FunctionDeclaration",
17+
"paramnames": [
18+
"field"
19+
]
20+
}
21+
},
22+
"params": [
23+
{
24+
"type": {
25+
"names": [
26+
"Array.<string>"
27+
]
28+
},
29+
"optional": true,
30+
"description": "...",
31+
"name": "field"
32+
}
33+
],
34+
"name": "myFunction",
35+
"longname": "myFunction",
36+
"kind": "function",
37+
"scope": "global",
38+
"___id": "T000002R000002",
39+
"___s": true
40+
},
41+
{
42+
"kind": "package",
43+
"longname": "package:undefined",
44+
"files": [
45+
"e:\\jsdoc-tsd\\exampleProject\\src\\test.js"
46+
],
47+
"___id": "T000002R000003",
48+
"___s": true
49+
}
50+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"comment": "/**\r\n * @param {Array} [field] ...\r\n */",
4+
"meta": {
5+
"range": [
6+
41,
7+
70
8+
],
9+
"filename": "test.js",
10+
"lineno": 4,
11+
"columnno": 0,
12+
"path": "e:\\jsdoc-tsd\\exampleProject\\src",
13+
"code": {
14+
"id": "astnode100000002",
15+
"name": "myFunction",
16+
"type": "FunctionDeclaration",
17+
"paramnames": [
18+
"field"
19+
]
20+
}
21+
},
22+
"params": [
23+
{
24+
"type": {
25+
"names": [
26+
"Array"
27+
]
28+
},
29+
"optional": true,
30+
"description": "...",
31+
"name": "field"
32+
}
33+
],
34+
"name": "myFunction",
35+
"longname": "myFunction",
36+
"kind": "function",
37+
"scope": "global",
38+
"___id": "T000002R000002",
39+
"___s": true
40+
},
41+
{
42+
"kind": "package",
43+
"longname": "package:undefined",
44+
"files": [
45+
"e:\\jsdoc-tsd\\exampleProject\\src\\test.js"
46+
],
47+
"___id": "T000002R000003",
48+
"___s": true
49+
}
50+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"comment": "/**\r\n * @param {Array.<Array.<Array>>} [field] ...\r\n */",
4+
"meta": {
5+
"range": [
6+
57,
7+
86
8+
],
9+
"filename": "test.js",
10+
"lineno": 4,
11+
"columnno": 0,
12+
"path": "e:\\jsdoc-tsd\\exampleProject\\src",
13+
"code": {
14+
"id": "astnode100000002",
15+
"name": "myFunction",
16+
"type": "FunctionDeclaration",
17+
"paramnames": [
18+
"field"
19+
]
20+
}
21+
},
22+
"params": [
23+
{
24+
"type": {
25+
"names": [
26+
"Array.<Array.<Array>>"
27+
]
28+
},
29+
"optional": true,
30+
"description": "...",
31+
"name": "field"
32+
}
33+
],
34+
"name": "myFunction",
35+
"longname": "myFunction",
36+
"kind": "function",
37+
"scope": "global",
38+
"___id": "T000002R000002",
39+
"___s": true
40+
},
41+
{
42+
"kind": "package",
43+
"longname": "package:undefined",
44+
"files": [
45+
"e:\\jsdoc-tsd\\exampleProject\\src\\test.js"
46+
],
47+
"___id": "T000002R000003",
48+
"___s": true
49+
}
50+
]

0 commit comments

Comments
 (0)