Summary
When an Avro schema has an array whose items are a union type, avroToTypeScript generates incorrect TypeScript syntax. The [] suffix is appended after the last union member only, rather than wrapping the full union in Array<...>. The result is a type that TypeScript parses differently from what was intended.
Reproduction
Avro schema:
{
"type": "record",
"name": "Analysis",
"fields": [
{
"name": "detections",
"type": ["null", {
"type": "array",
"items": [
{ "type": "record", "name": "MotionDetection", "fields": [] },
{ "type": "record", "name": "PersonDetection", "fields": [] },
{ "type": "record", "name": "FaceDetection", "fields": [] }
]
}],
"default": null
}
]
}
Generated output (incorrect):
export interface Analysis {
detections: null | MotionDetection | PersonDetection | FaceDetection[];
}
Expected output:
export interface Analysis {
detections: null | Array<MotionDetection | PersonDetection | FaceDetection>;
}
Why this is wrong
TypeScript parses null | MotionDetection | PersonDetection | FaceDetection[] as:
null | MotionDetection | PersonDetection | (FaceDetection[])
The array type applies only to FaceDetection, not to the full union. A detections value of type MotionDetection[] would be incorrectly accepted since MotionDetection (non-array) is a union member.
Root cause
In convertType, when isArrayType is true, the items type is converted recursively and [] is appended:
else if (isArrayType(type)) {
return convertType(type.items, buffer, opts) + "[]";
}
When type.items is itself a union (an array in Avro schema), convertType returns a |-joined string and [] is appended to the last member only.
Fix
When items is a union (i.e. the result contains |), wrap in Array<...> instead of appending []:
else if (isArrayType(type)) {
const itemType = convertType(type.items, buffer, opts);
return itemType.includes(' | ') ? `Array<${itemType}>` : `${itemType}[]`;
}
Environment
- avro-typescript version: 1.3.0
- Node.js: 20.11.1
Summary
When an Avro schema has an array whose items are a union type,
avroToTypeScriptgenerates incorrect TypeScript syntax. The[]suffix is appended after the last union member only, rather than wrapping the full union inArray<...>. The result is a type that TypeScript parses differently from what was intended.Reproduction
Avro schema:
{ "type": "record", "name": "Analysis", "fields": [ { "name": "detections", "type": ["null", { "type": "array", "items": [ { "type": "record", "name": "MotionDetection", "fields": [] }, { "type": "record", "name": "PersonDetection", "fields": [] }, { "type": "record", "name": "FaceDetection", "fields": [] } ] }], "default": null } ] }Generated output (incorrect):
Expected output:
Why this is wrong
TypeScript parses
null | MotionDetection | PersonDetection | FaceDetection[]as:The array type applies only to
FaceDetection, not to the full union. Adetectionsvalue of typeMotionDetection[]would be incorrectly accepted sinceMotionDetection(non-array) is a union member.Root cause
In
convertType, whenisArrayTypeis true, the items type is converted recursively and[]is appended:When
type.itemsis itself a union (an array in Avro schema),convertTypereturns a|-joined string and[]is appended to the last member only.Fix
When items is a union (i.e. the result contains
|), wrap inArray<...>instead of appending[]:Environment