Skip to content

Commit 4cb29a1

Browse files
committed
fix: handle untyped functions and simple case of inferring type from argument default
- Work on inferring still in progress, arg with type function is not handled; + see the existing vega#2283
1 parent c3b9504 commit 4cb29a1

File tree

11 files changed

+335
-3
lines changed

11 files changed

+335
-3
lines changed

factory/parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export function createParser(program: ts.Program, config: CompletedConfig, augme
114114
.addNodeParser(new ObjectTypeNodeParser())
115115
.addNodeParser(new AsExpressionNodeParser(chainNodeParser))
116116
.addNodeParser(new SatisfiesNodeParser(chainNodeParser))
117-
.addNodeParser(withJsDoc(new ParameterParser(chainNodeParser)))
117+
.addNodeParser(withJsDoc(new ParameterParser(typeChecker, chainNodeParser)))
118118
.addNodeParser(new StringLiteralNodeParser())
119119
.addNodeParser(new StringTemplateLiteralNodeParser(chainNodeParser))
120120
.addNodeParser(new IntrinsicNodeParser())

src/NodeParser/ParameterParser.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,33 @@ import type { NodeParser } from "../NodeParser.js";
33
import type { Context } from "../NodeParser.js";
44
import type { SubNodeParser } from "../SubNodeParser.js";
55
import type { BaseType } from "../Type/BaseType.js";
6+
import { AnyType } from "../Type/AnyType.js";
67

78
export class ParameterParser implements SubNodeParser {
8-
constructor(protected childNodeParser: NodeParser) {}
9+
public constructor(
10+
protected typeChecker: ts.TypeChecker,
11+
protected childNodeParser: NodeParser,
12+
) {}
913

1014
public supportsNode(node: ts.ParameterDeclaration): boolean {
1115
return node.kind === ts.SyntaxKind.Parameter;
1216
}
13-
public createType(node: ts.FunctionTypeNode, context: Context): BaseType {
17+
public createType(node: ts.ParameterDeclaration, context: Context): BaseType {
18+
if (!node.type) {
19+
const inferredTypeNode = this.typeChecker.typeToTypeNode(
20+
this.typeChecker.getTypeAtLocation(node),
21+
node,
22+
ts.NodeBuilderFlags.NoTruncation,
23+
);
24+
25+
if (inferredTypeNode) {
26+
return this.childNodeParser.createType(inferredTypeNode, context);
27+
}
28+
29+
// Parameters without an explicit or inferred type default to `any`.
30+
return new AnyType();
31+
}
32+
1433
return this.childNodeParser.createType(node.type, context);
1534
}
1635
}

test/valid-data-other.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ describe("valid-data-other", () => {
1313
it("exported-enums-union", assertValidSchema("exported-enums-union", "MyObject"));
1414

1515
it("function-parameters-default-value", assertValidSchema("function-parameters-default-value", "myFunction"));
16+
it("function-parameters-default-infer", assertValidSchema("function-parameters-default-infer", "myFunction"));
1617
it("function-parameters-declaration", assertValidSchema("function-parameters-declaration", "myFunction"));
18+
it("function-parameters-infer-type", assertValidSchema("function-parameters-infer-type", "myFunction"));
1719
it("function-parameters-jsdoc", assertValidSchema("function-parameters-jsdoc", "myFunction", { jsDoc: "basic" }));
1820
it("function-parameters-optional", assertValidSchema("function-parameters-optional", "myFunction"));
1921
it("function-parameters-required", assertValidSchema("function-parameters-required", "myFunction"));
2022
it(
2123
"function-parameters-variable-assignment",
2224
assertValidSchema("function-parameters-variable-assignment", "myFunction"),
2325
);
26+
it("function-parameters-untyped", assertValidSchema("function-parameters-untyped", "myFunction"));
2427
it("function-function-syntax", assertValidSchema("function-function-syntax", "myFunction"));
2528

2629
it("string-literals", assertValidSchema("string-literals", "MyObject"));
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const myFunction = (paramWithDefault = "something") => {
2+
return "whatever";
3+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$ref": "#/definitions/myFunction",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"definitions": {
5+
"myFunction": {
6+
"$comment": "(paramWithDefault = \"something\") =>undefined",
7+
"properties": {
8+
"namedArgs": {
9+
"additionalProperties": false,
10+
"properties": {
11+
"paramWithDefault": {
12+
"type": "string"
13+
}
14+
},
15+
"type": "object"
16+
}
17+
},
18+
"type": "object"
19+
}
20+
}
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { myObj, MyClass, myArray, mFunction } from "./module";
2+
3+
export const myFunction = (
4+
str = "something",
5+
num = 123,
6+
bool = true,
7+
[a, b, c] = [1, 2, 3],
8+
obj = { a: 1, b: 2 },
9+
clas = new MyClass(),
10+
func = (a: number, b: number) => a + b, // fails
11+
object = myObj, // fails
12+
func1 = mFunction, // fails
13+
arr = myArray, // fails
14+
) => {
15+
return "whatever";
16+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export function mFunction(input = "something") {
2+
return "whatever";
3+
}
4+
5+
export class MyClass {
6+
someField = "something";
7+
}
8+
9+
export const myObj = {
10+
str: "str",
11+
num: 123,
12+
func: ({ a, b } = { a: 1, b: "2" }) => "whatever",
13+
};
14+
15+
export const myArray = ["str", 123, () => "whatever"];
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
{
2+
"$ref": "#/definitions/myFunction",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"definitions": {
5+
"MyClass": {
6+
"additionalProperties": false,
7+
"properties": {
8+
"someField": {
9+
"type": "string"
10+
}
11+
},
12+
"required": [
13+
"someField"
14+
],
15+
"type": "object"
16+
},
17+
"myFunction": {
18+
"$comment": "(\n str = \"something\",\n num = 123,\n bool = true,\n [a, b, c] = [1, 2, 3],\n obj = { a: 1, b: 2 },\n func = (a: number, b: number) => a + b,\n object = myObj,\n func1 = mFunction,\n clas = new MyClass(),\n arr = myArray) =>undefined",
19+
"properties": {
20+
"namedArgs": {
21+
"additionalProperties": false,
22+
"properties": {
23+
"[a, b, c]": {
24+
"items": {
25+
"type": "number"
26+
},
27+
"maxItems": 3,
28+
"minItems": 3,
29+
"type": "array"
30+
},
31+
"arr": {
32+
"items": {
33+
"anyOf": [
34+
{
35+
"type": "string"
36+
},
37+
{
38+
"type": "number"
39+
},
40+
{
41+
"$comment": "Function"
42+
}
43+
]
44+
},
45+
"type": "array"
46+
},
47+
"bool": {
48+
"type": "boolean"
49+
},
50+
"clas": {
51+
"$ref": "#/definitions/MyClass"
52+
},
53+
"func": {
54+
"$comment": "Function",
55+
"properties": {
56+
"namedArgs": {
57+
"additionalProperties": false,
58+
"properties": {
59+
"a": {
60+
"type": "number"
61+
},
62+
"b": {
63+
"type": "number"
64+
}
65+
},
66+
"required": [
67+
"a",
68+
"b"
69+
],
70+
"type": "object"
71+
}
72+
},
73+
"type": "object"
74+
},
75+
"func1": {
76+
"$comment": "Function",
77+
"properties": {
78+
"namedArgs": {
79+
"additionalProperties": false,
80+
"properties": {
81+
"input": {
82+
"type": "string"
83+
}
84+
},
85+
"type": "object"
86+
}
87+
},
88+
"type": "object"
89+
},
90+
"num": {
91+
"type": "number"
92+
},
93+
"obj": {
94+
"additionalProperties": false,
95+
"properties": {
96+
"a": {
97+
"type": "number"
98+
},
99+
"b": {
100+
"type": "number"
101+
}
102+
},
103+
"required": [
104+
"a",
105+
"b"
106+
],
107+
"type": "object"
108+
},
109+
"object": {
110+
"additionalProperties": false,
111+
"properties": {
112+
"func": {
113+
"$comment": "Function",
114+
"properties": {
115+
"namedArgs": {
116+
"additionalProperties": false,
117+
"properties": {
118+
"_0": {
119+
"additionalProperties": false,
120+
"properties": {
121+
"a": {
122+
"type": "number"
123+
},
124+
"b": {
125+
"type": "string"
126+
}
127+
},
128+
"required": [
129+
"a",
130+
"b"
131+
],
132+
"type": "object"
133+
}
134+
},
135+
"type": "object"
136+
}
137+
},
138+
"type": "object"
139+
},
140+
"num": {
141+
"type": "number"
142+
},
143+
"str": {
144+
"type": "string"
145+
}
146+
},
147+
"required": [
148+
"str",
149+
"num",
150+
"func"
151+
],
152+
"type": "object"
153+
},
154+
"str": {
155+
"type": "string"
156+
}
157+
},
158+
"type": "object"
159+
}
160+
},
161+
"type": "object"
162+
}
163+
}
164+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"$ref": "#/definitions/myFunction",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"definitions": {
5+
"MyClass": {
6+
"additionalProperties": false,
7+
"properties": {
8+
"someField": {
9+
"type": "string"
10+
}
11+
},
12+
"required": [
13+
"someField"
14+
],
15+
"type": "object"
16+
},
17+
"myFunction": {
18+
"$comment": "(\n str = \"something\",\n num = 123,\n bool = true,\n [a, b, c] = [1, 2, 3],\n obj = { a: 1, b: 2 },\n // func = (a: number, b: number) => a + b,\n // object = myObj,\n // func1 = mFunction,\n clas = new MyClass()) =>undefined",
19+
"properties": {
20+
"namedArgs": {
21+
"additionalProperties": false,
22+
"properties": {
23+
"[a, b, c]": {
24+
"items": {
25+
"type": "number"
26+
},
27+
"maxItems": 3,
28+
"minItems": 3,
29+
"type": "array"
30+
},
31+
"bool": {
32+
"type": "boolean"
33+
},
34+
"clas": {
35+
"$ref": "#/definitions/MyClass"
36+
},
37+
"num": {
38+
"type": "number"
39+
},
40+
"obj": {
41+
"additionalProperties": false,
42+
"properties": {
43+
"a": {
44+
"type": "number"
45+
},
46+
"b": {
47+
"type": "number"
48+
}
49+
},
50+
"required": [
51+
"a",
52+
"b"
53+
],
54+
"type": "object"
55+
},
56+
"str": {
57+
"type": "string"
58+
}
59+
},
60+
"type": "object"
61+
}
62+
},
63+
"type": "object"
64+
}
65+
}
66+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const myFunction = (untyped) => {
2+
return "whatever";
3+
};

0 commit comments

Comments
 (0)