Skip to content

Commit e3eb2c4

Browse files
committed
fix: support union types
1 parent 6b22580 commit e3eb2c4

File tree

2 files changed

+97
-5
lines changed

2 files changed

+97
-5
lines changed

src/utils/schema.ts

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ function convertVueTypeToJsonSchema(vueType: string, vueSchema: PropertyMetaSche
7979
return convertEnumToJsonSchema(vueType, vueSchema)
8080
}
8181

82+
// Handle union types when schema is a string (e.g., "string | number | symbol")
83+
if (typeof vueSchema === 'string' && vueSchema.includes('|')) {
84+
return convertUnionTypeFromString(vueSchema)
85+
}
86+
8287
// Unwrap enums for optionals/unions
8388
const { type: unwrappedType, schema: unwrappedSchema, enumValues } = unwrapEnumSchema(vueType, vueSchema)
8489
if (enumValues && unwrappedType === 'boolean') {
@@ -238,6 +243,8 @@ function convertSimpleType(type: string): any {
238243
return { type: 'number' }
239244
case 'boolean':
240245
return { type: 'boolean' }
246+
case 'symbol':
247+
return { type: 'string' } // JSON Schema doesn't have symbol type, map to string
241248
case 'object':
242249
return { type: 'object' }
243250
case 'array':
@@ -366,7 +373,13 @@ function isEnumType(vueType: string, vueSchema: PropertyMetaSchema): boolean {
366373
v !== 'undefined' &&
367374
(v === 'true' || v === 'false')
368375
)
369-
return stringLiterals.length > 0 || booleanLiterals.length > 0
376+
// Check if all non-undefined values are primitive types
377+
const primitiveTypes = values.filter(v =>
378+
v !== 'undefined' &&
379+
typeof v === 'string' &&
380+
['string', 'number', 'boolean', 'symbol'].includes(v)
381+
)
382+
return stringLiterals.length > 0 || booleanLiterals.length > 0 || primitiveTypes.length > 0
370383
}
371384
}
372385

@@ -408,6 +421,8 @@ function convertEnumToJsonSchema(vueType: string, vueSchema: PropertyMetaSchema)
408421
types.add('number')
409422
} else if (value === 'boolean') {
410423
types.add('boolean')
424+
} else if (value === 'symbol') {
425+
types.add('symbol') // Keep symbol as distinct type for now
411426
}
412427
} else if (typeof value === 'object' && value !== null) {
413428
// Complex type like (string & {}) - convert to allOf schema
@@ -452,9 +467,13 @@ function convertEnumToJsonSchema(vueType: string, vueSchema: PropertyMetaSchema)
452467

453468
// Add type if it's consistent
454469
if (types.size === 1) {
455-
result.type = Array.from(types)[0]
470+
const type = Array.from(types)[0]
471+
result.type = type === 'symbol' ? 'string' : type
456472
} else if (types.size > 1) {
457-
result.type = Array.from(types)
473+
const mappedTypes = Array.from(types).map(type => type === 'symbol' ? 'string' : type)
474+
// Remove duplicates after mapping
475+
const uniqueTypes = [...new Set(mappedTypes)]
476+
result.type = uniqueTypes.length === 1 ? uniqueTypes[0] : uniqueTypes
458477
}
459478

460479
// Special case: if it's a boolean enum with just true/false, treat as regular boolean
@@ -468,9 +487,13 @@ function convertEnumToJsonSchema(vueType: string, vueSchema: PropertyMetaSchema)
468487

469488
// If no enum values but we have types, create a union type
470489
if (types.size > 1) {
471-
return { type: Array.from(types) }
490+
const mappedTypes = Array.from(types).map(type => type === 'symbol' ? 'string' : type)
491+
// Remove duplicates after mapping
492+
const uniqueTypes = [...new Set(mappedTypes)]
493+
return { type: uniqueTypes.length === 1 ? uniqueTypes[0] : uniqueTypes }
472494
} else if (types.size === 1) {
473-
return { type: Array.from(types)[0] }
495+
const type = Array.from(types)[0]
496+
return { type: type === 'symbol' ? 'string' : type }
474497
}
475498
}
476499
}
@@ -575,4 +598,26 @@ function convertIntersectionType(typeString: string): any | null {
575598
}
576599

577600
return null
601+
}
602+
603+
/**
604+
* Convert union type from string to JSON Schema
605+
*/
606+
function convertUnionTypeFromString(unionString: string): any {
607+
const types = unionString.split('|').map(t => t.trim())
608+
const jsonTypes = types.map(type => {
609+
if (type === 'symbol') {
610+
return 'string' // JSON Schema doesn't have symbol type, map to string
611+
}
612+
return type
613+
})
614+
615+
// Remove duplicates
616+
const uniqueTypes = [...new Set(jsonTypes)]
617+
618+
if (uniqueTypes.length === 1) {
619+
return { type: uniqueTypes[0] }
620+
} else {
621+
return { type: uniqueTypes }
622+
}
578623
}

test/enum.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,4 +1682,51 @@ describe('Enum Support', () => {
16821682
})
16831683
})
16841684

1685+
test('should handle string | number | symbol union type', () => {
1686+
const unionTypeData = [
1687+
{
1688+
"name": "activeColor",
1689+
"global": false,
1690+
"description": "",
1691+
"tags": [],
1692+
"required": false,
1693+
"type": "string | number | symbol",
1694+
"schema": {
1695+
"kind": "enum",
1696+
"type": "string | number | symbol",
1697+
"schema": {
1698+
"0": "string",
1699+
"1": "number",
1700+
"2": "symbol"
1701+
}
1702+
}
1703+
}
1704+
]
1705+
1706+
const jsonSchema = propsToJsonSchema(unionTypeData as any)
1707+
1708+
expect(jsonSchema.properties?.activeColor).toEqual({
1709+
type: ["string", "number"] // symbol maps to string and gets deduplicated
1710+
})
1711+
})
1712+
1713+
test('should handle string | number | symbol union type from string schema', () => {
1714+
const unionTypeData = [
1715+
{
1716+
"name": "activeColor",
1717+
"global": false,
1718+
"description": "",
1719+
"tags": [],
1720+
"required": false,
1721+
"type": "string | number | symbol",
1722+
"schema": "string | number | symbol"
1723+
}
1724+
]
1725+
1726+
const jsonSchema = propsToJsonSchema(unionTypeData as any)
1727+
1728+
expect(jsonSchema.properties?.activeColor).toEqual({
1729+
type: ["string", "number"] // symbol maps to string in JSON Schema
1730+
})
1731+
})
16851732
})

0 commit comments

Comments
 (0)