Skip to content

Commit 3e505c5

Browse files
committed
don't include callables/function-like things in json schemas
1 parent b3e7eaf commit 3e505c5

File tree

3 files changed

+41
-79
lines changed

3 files changed

+41
-79
lines changed

packages/js-runtime/test/fixtures/handler-schema/date-and-map-types.expected.ts

Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -25,47 +25,11 @@ const timedHandler = handler({
2525
Map: {
2626
type: "object",
2727
properties: {
28-
clear: {
29-
type: "object",
30-
properties: {}
31-
},
32-
delete: {
33-
type: "object",
34-
properties: {}
35-
},
36-
forEach: {
37-
type: "object",
38-
properties: {}
39-
},
40-
get: {
41-
type: "object",
42-
properties: {}
43-
},
44-
has: {
45-
type: "object",
46-
properties: {}
47-
},
48-
set: {
49-
type: "object",
50-
properties: {}
51-
},
5228
size: {
5329
type: "number"
54-
},
55-
entries: {
56-
type: "object",
57-
properties: {}
58-
},
59-
keys: {
60-
type: "object",
61-
properties: {}
62-
},
63-
values: {
64-
type: "object",
65-
properties: {}
6630
}
6731
},
68-
required: ["clear", "delete", "forEach", "get", "has", "set", "size", "entries", "keys", "values"]
32+
required: ["size"]
6933
}
7034
}
7135
} as const satisfies JSONSchema, {
@@ -85,47 +49,11 @@ const timedHandler = handler({
8549
Map: {
8650
type: "object",
8751
properties: {
88-
clear: {
89-
type: "object",
90-
properties: {}
91-
},
92-
delete: {
93-
type: "object",
94-
properties: {}
95-
},
96-
forEach: {
97-
type: "object",
98-
properties: {}
99-
},
100-
get: {
101-
type: "object",
102-
properties: {}
103-
},
104-
has: {
105-
type: "object",
106-
properties: {}
107-
},
108-
set: {
109-
type: "object",
110-
properties: {}
111-
},
11252
size: {
11353
type: "number"
114-
},
115-
entries: {
116-
type: "object",
117-
properties: {}
118-
},
119-
keys: {
120-
type: "object",
121-
properties: {}
122-
},
123-
values: {
124-
type: "object",
125-
properties: {}
12654
}
12755
},
128-
required: ["clear", "delete", "forEach", "get", "has", "set", "size", "entries", "keys", "values"]
56+
required: ["size"]
12957
}
13058
}
13159
} as const satisfies JSONSchema, (event, state) => {

packages/schema-generator/src/formatters/object-formatter.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {
44
SchemaDefinition,
55
TypeFormatter,
66
} from "../interface.ts";
7-
import { safeGetPropertyType } from "../type-utils.ts";
7+
import { isFunctionLike, safeGetPropertyType } from "../type-utils.ts";
88
import type { SchemaGenerator } from "../schema-generator.ts";
99
import { extractDocFromSymbolAndDecls, getDeclDocs } from "../doc-utils.ts";
1010
import { getLogger } from "@commontools/utils/logger";
@@ -66,20 +66,25 @@ export class ObjectFormatter implements TypeFormatter {
6666
const propName = prop.getName();
6767
if (propName.startsWith("__")) continue; // Skip internal properties
6868

69-
const isOptional = (prop.flags & ts.SymbolFlags.Optional) !== 0;
70-
if (!isOptional) required.push(propName);
71-
7269
let propTypeNode: ts.TypeNode | undefined;
7370
const propDecl = prop.valueDeclaration ??
74-
(prop.declarations?.[0] as ts.Declaration);
71+
(prop.declarations?.[0] as ts.Declaration | undefined);
72+
7573
if (propDecl) {
74+
if (
75+
ts.isMethodSignature(propDecl) || ts.isMethodDeclaration(propDecl)
76+
) {
77+
continue;
78+
}
7679
if (
7780
ts.isPropertySignature(propDecl) || ts.isPropertyDeclaration(propDecl)
7881
) {
7982
if (propDecl.type) propTypeNode = propDecl.type as ts.TypeNode;
8083
}
8184
}
8285

86+
if ((prop.flags & ts.SymbolFlags.Method) !== 0) continue;
87+
8388
// Get the actual property type and recursively delegate to the main schema generator
8489
const resolvedPropType = safeGetPropertyType(
8590
prop,
@@ -88,6 +93,13 @@ export class ObjectFormatter implements TypeFormatter {
8893
propTypeNode,
8994
);
9095

96+
if (isFunctionLike(resolvedPropType)) {
97+
continue;
98+
}
99+
100+
const isOptional = (prop.flags & ts.SymbolFlags.Optional) !== 0;
101+
if (!isOptional) required.push(propName);
102+
91103
// Delegate to the main generator (specific formatters handle wrappers/defaults)
92104
const generated: SchemaDefinition = this.schemaGenerator.formatChildType(
93105
resolvedPropType,

packages/schema-generator/src/type-utils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,28 @@ export function getNamedTypeKey(
165165
return name;
166166
}
167167

168+
/**
169+
* Determine if a type represents a callable/constructable function value.
170+
*/
171+
export function isFunctionLike(type: ts.Type): boolean {
172+
if (type.getCallSignatures().length > 0) return true;
173+
if (type.getConstructSignatures().length > 0) return true;
174+
175+
const symbol = type.symbol;
176+
if (!symbol) return false;
177+
178+
const flags = symbol.flags;
179+
if (
180+
(flags & ts.SymbolFlags.Function) !== 0 ||
181+
(flags & ts.SymbolFlags.Method) !== 0 ||
182+
(flags & ts.SymbolFlags.Signature) !== 0
183+
) {
184+
return true;
185+
}
186+
187+
return false;
188+
}
189+
168190
/**
169191
* Helper to extract array element type using multiple detection methods
170192
*/

0 commit comments

Comments
 (0)