Avro TypeScript Generator
avro-typescript is a dedicated library for generating TypeScript interfaces from Apache Avro schemas. It takes an Avro schema, typically provided as a JavaScript object parsed from JSON, and outputs the corresponding TypeScript code as a string. The library is currently at version 1.3.0 and appears to be actively maintained, with recent updates addressing issues like top-level enum support. It supports most standard Avro features, including enumerated types, maps, named records, unions, and primitives, along with mandatory and optional fields. A key differentiator is its ability to override logical Avro types (e.g., converting an Avro `int` with a `date` logical type to a TypeScript `Date` object) by passing a mapping in the options. This tool operates effectively in both Node.js and browser environments, focusing solely on type generation rather than schema parsing or serialization/deserialization, which often relies on companion libraries like `avsc`.
Common errors
-
TS2503: Cannot find namespace 'MyAvroNamespace'
cause This error often occurs when you're trying to reference a TypeScript type that you expect to be wrapped in a namespace corresponding to an Avro namespace, but `avro-typescript` (as of current versions) does not generate explicit TypeScript namespaces. Alternatively, it can happen if types generated by `avro-typescript` are used in conjunction with types generated by other Avro tools that *do* create namespaces, leading to a mismatch.fixEnsure that the generated TypeScript types are directly accessible without a namespace prefix, or if using a mix of generation tools, adjust your imports or manually wrap `avro-typescript` generated types into the expected namespace. Consider using unique type names across different Avro namespaces if direct namespace support is not available from your chosen generator. -
org.apache.avro.SchemaParseException: Undefined name: MyTypeName
cause While not a direct TypeScript error, this is a common Avro schema parsing error that arises when the Avro schema itself is invalid, often due to a type referencing another type (`MyTypeName`) that hasn't been defined within the current schema or an imported schema. If `avro-typescript` receives such an invalid schema, its generation might fail or produce incorrect output.fixValidate your Avro schema independently using an Avro schema parser or linter before feeding it to `avro-typescript`. Ensure all referenced named types (records, enums, fixed) are correctly defined within the schema or imported if they reside in separate files (though `avro-typescript` expects a single, resolved schema object).
Warnings
- gotcha The library does not currently provide explicit support for Avro namespaces in the generated TypeScript output (e.g., creating nested modules or namespaces). While the Avro schema itself might define a `namespace`, the output interfaces will typically be at the top level or derive their names directly, potentially leading to name collisions if not managed carefully by the user. Other generators like `@ovotech/avro-ts` offer explicit namespace handling.
- gotcha When using Avro logical types (e.g., `date`, `timestamp-millis`, `decimal`), `avro-typescript` will default to the underlying primitive Avro type (e.g., `int`, `long`, `bytes`) in the generated TypeScript if no `logicalTypes` override is provided in the options. This can lead to less precise types (e.g., `number` instead of `Date`).
- gotcha Avro schemas can define default values for fields, implying they are optional. While `avro-typescript` handles `null` in unions to make fields optional, it does not currently generate TypeScript types that reflect all Avro default values as optional fields in the interface. The `To-do` list indicates a future feature for 'Generate a function to set defaults as per the schema', suggesting full default value integration might be incomplete.
Install
-
npm install avro-typescript -
yarn add avro-typescript -
pnpm add avro-typescript
Imports
- avroToTypeScript
const avroToTypeScript = require('avro-typescript');import { avroToTypeScript } from 'avro-typescript'; - RecordType
import { RecordType } from 'avro-typescript/dist/types';import { RecordType } from 'avro-typescript'; - Options
import { Options } from 'avro-typescript';
Quickstart
import { avroToTypeScript, RecordType } from 'avro-typescript';
import * as fs from 'fs';
import * as path from 'path';
// Define a sample Avro schema (typically loaded from a .avsc file)
const avroSchema: RecordType = {
type: 'record',
name: 'UserProfile',
namespace: 'com.example.user',
fields: [
{ name: 'id', type: 'string' },
{ name: 'username', type: 'string' },
{
name: 'email',
type: ['null', 'string'],
default: null,
},
{
name: 'createdAt',
type: { type: 'long', logicalType: 'timestamp-millis' },
},
{
name: 'status',
type: {
type: 'enum',
name: 'UserStatus',
symbols: ['ACTIVE', 'INACTIVE', 'PENDING'],
},
default: 'PENDING',
},
{
name: 'tags',
type: { type: 'array', items: 'string' },
default: [],
},
],
};
// Options for generating TypeScript, including logical type overrides
const generationOptions = {
logicalTypes: {
'timestamp-millis': 'Date',
},
};
// Generate TypeScript code
const typescriptCode = avroToTypeScript(avroSchema, generationOptions);
console.log('Generated TypeScript:\n');
console.log(typescriptCode);
// Example of writing to a file (optional)
const outputPath = path.join(process.cwd(), 'generated-types.d.ts');
fs.writeFileSync(outputPath, typescriptCode);
console.log(`\nTypeScript types written to ${outputPath}`);
/*
Expected output (simplified):
Generated TypeScript:
export namespace com.example.user {
export interface UserProfile {
id: string;
username: string;
email: string | null;
createdAt: Date;
status: UserStatus;
tags: string[];
}
export type UserStatus = 'ACTIVE' | 'INACTIVE' | 'PENDING';
}
TypeScript types written to .../generated-types.d.ts
*/