or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-parsing.mdcli-usage.mdcustom-extensions.mdindex.mdprogram-management.mdschema-generation.mdtype-formatting.md

custom-extensions.mddocs/

0

# Custom Extensions

1

2

Create custom parsers and formatters to handle special TypeScript constructs, generate custom JSON schema formats, or integrate with specific validation systems.

3

4

## Capabilities

5

6

### Custom Type Formatters

7

8

Create custom formatters to control how internal types are converted to JSON schema definitions.

9

10

```typescript { .api }

11

interface SubTypeFormatter extends TypeFormatter {

12

/**

13

* Check if this formatter can handle the given type

14

* @param type - Internal type representation to check

15

* @returns true if this formatter supports the type

16

*/

17

supportsType(type: BaseType): boolean;

18

19

/**

20

* Generate JSON schema definition for a type

21

* @param type - Internal type representation

22

* @returns JSON schema definition object

23

*/

24

getDefinition(type: BaseType): Definition;

25

26

/**

27

* Get child types that need definitions

28

* @param type - Internal type representation

29

* @returns Array of child types

30

*/

31

getChildren(type: BaseType): BaseType[];

32

}

33

```

34

35

**Custom Formatter Example:**

36

37

```typescript

38

import {

39

SubTypeFormatter,

40

BaseType,

41

Definition,

42

FunctionType,

43

TypeFormatter

44

} from "ts-json-schema-generator";

45

46

export class MyFunctionTypeFormatter implements SubTypeFormatter {

47

constructor(private childTypeFormatter: TypeFormatter) {}

48

49

public supportsType(type: BaseType): boolean {

50

return type instanceof FunctionType;

51

}

52

53

public getDefinition(type: FunctionType): Definition {

54

// Custom schema for function properties

55

return {

56

type: "object",

57

properties: {

58

isFunction: {

59

type: "boolean",

60

const: true,

61

},

62

signature: {

63

type: "string",

64

description: "Function signature"

65

}

66

},

67

};

68

}

69

70

public getChildren(type: FunctionType): BaseType[] {

71

// Return empty if no child types, or delegate to child formatter

72

return [];

73

}

74

}

75

```

76

77

### Custom Node Parsers

78

79

Create custom parsers to handle new TypeScript syntax or convert special constructs to internal types.

80

81

```typescript { .api }

82

interface SubNodeParser extends NodeParser {

83

/**

84

* Check if this parser can handle the given AST node

85

* @param node - TypeScript AST node to check

86

* @returns true if this parser supports the node type

87

*/

88

supportsNode(node: ts.Node): boolean;

89

90

/**

91

* Convert a TypeScript AST node to internal type representation

92

* @param node - TypeScript AST node to parse

93

* @param context - Parsing context

94

* @param reference - Optional reference type

95

* @returns Internal type representation

96

*/

97

createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType;

98

}

99

```

100

101

**Custom Parser Example:**

102

103

```typescript

104

import {

105

SubNodeParser,

106

BaseType,

107

Context,

108

StringType,

109

ReferenceType

110

} from "ts-json-schema-generator";

111

import ts from "ts-json-schema-generator";

112

113

export class MyConstructorParser implements SubNodeParser {

114

supportsNode(node: ts.Node): boolean {

115

return node.kind === ts.SyntaxKind.ConstructorType;

116

}

117

118

createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType {

119

// Treat constructor types as strings for this example

120

return new StringType();

121

}

122

}

123

```

124

125

### Formatter Integration

126

127

Add custom formatters to the main formatter using the augmentation callback.

128

129

```typescript { .api }

130

type FormatterAugmentor = (

131

formatter: MutableTypeFormatter,

132

circularReferenceTypeFormatter: CircularReferenceTypeFormatter

133

) => void;

134

135

function createFormatter(

136

config: Config,

137

augmentor?: FormatterAugmentor

138

): TypeFormatter;

139

```

140

141

**Integration Example:**

142

143

```typescript

144

import {

145

createProgram,

146

createParser,

147

SchemaGenerator,

148

createFormatter

149

} from "ts-json-schema-generator";

150

import { MyFunctionTypeFormatter } from "./my-function-formatter.js";

151

152

const config = {

153

path: "path/to/source/file.ts",

154

tsconfig: "path/to/tsconfig.json",

155

type: "*",

156

};

157

158

// Configure formatter with custom extension

159

const formatter = createFormatter(config, (fmt, circularReferenceTypeFormatter) => {

160

// Add custom formatter to the chain

161

fmt.addTypeFormatter(new MyFunctionTypeFormatter(circularReferenceTypeFormatter));

162

});

163

164

const program = createProgram(config);

165

const parser = createParser(program, config);

166

const generator = new SchemaGenerator(program, parser, formatter, config);

167

const schema = generator.createSchema(config.type);

168

```

169

170

### Parser Integration

171

172

Add custom parsers to the main parser using the augmentation callback.

173

174

```typescript { .api }

175

type ParserAugmentor = (parser: MutableParser) => void;

176

177

function createParser(

178

program: ts.Program,

179

config: Config,

180

augmentor?: ParserAugmentor

181

): NodeParser;

182

```

183

184

**Integration Example:**

185

186

```typescript

187

import {

188

createProgram,

189

createParser,

190

SchemaGenerator,

191

createFormatter

192

} from "ts-json-schema-generator";

193

import { MyConstructorParser } from "./my-constructor-parser.js";

194

195

const config = {

196

path: "path/to/source/file.ts",

197

tsconfig: "path/to/tsconfig.json",

198

type: "*",

199

};

200

201

const program = createProgram(config);

202

203

// Configure parser with custom extension

204

const parser = createParser(program, config, (prs) => {

205

prs.addNodeParser(new MyConstructorParser());

206

});

207

208

const formatter = createFormatter(config);

209

const generator = new SchemaGenerator(program, parser, formatter, config);

210

const schema = generator.createSchema(config.type);

211

```

212

213

### Complete Custom Extension Example

214

215

Full example showing both custom parser and formatter working together.

216

217

```typescript

218

// custom-date-parser.ts

219

import {

220

SubNodeParser,

221

BaseType,

222

Context,

223

ReferenceType

224

} from "ts-json-schema-generator";

225

import ts from "ts-json-schema-generator";

226

227

// Custom type for Date objects

228

export class DateType extends BaseType {

229

public getId(): string {

230

return "date";

231

}

232

}

233

234

export class CustomDateParser implements SubNodeParser {

235

supportsNode(node: ts.Node): boolean {

236

// Handle references to Date type

237

return ts.isTypeReferenceNode(node) &&

238

ts.isIdentifier(node.typeName) &&

239

node.typeName.text === "Date";

240

}

241

242

createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType {

243

return new DateType();

244

}

245

}

246

247

// custom-date-formatter.ts

248

import {

249

SubTypeFormatter,

250

BaseType,

251

Definition

252

} from "ts-json-schema-generator";

253

254

export class CustomDateFormatter implements SubTypeFormatter {

255

supportsType(type: BaseType): boolean {

256

return type instanceof DateType;

257

}

258

259

getDefinition(type: DateType): Definition {

260

return {

261

type: "string",

262

format: "date-time",

263

description: "ISO 8601 date-time string"

264

};

265

}

266

267

getChildren(type: DateType): BaseType[] {

268

return [];

269

}

270

}

271

272

// usage.ts

273

import {

274

createProgram,

275

createParser,

276

createFormatter,

277

SchemaGenerator

278

} from "ts-json-schema-generator";

279

import { CustomDateParser } from "./custom-date-parser.js";

280

import { CustomDateFormatter } from "./custom-date-formatter.js";

281

282

const config = {

283

path: "src/api.ts",

284

type: "UserProfile"

285

};

286

287

const program = createProgram(config);

288

289

const parser = createParser(program, config, (prs) => {

290

prs.addNodeParser(new CustomDateParser());

291

});

292

293

const formatter = createFormatter(config, (fmt, circularRef) => {

294

fmt.addTypeFormatter(new CustomDateFormatter());

295

});

296

297

const generator = new SchemaGenerator(program, parser, formatter, config);

298

const schema = generator.createSchema(config.type);

299

300

console.log(JSON.stringify(schema, null, 2));

301

```

302

303

### Advanced Formatter Patterns

304

305

Complex formatter scenarios for specialized JSON schema generation.

306

307

**Formatter with Child Types:**

308

309

```typescript

310

export class ComplexTypeFormatter implements SubTypeFormatter {

311

constructor(private childTypeFormatter: TypeFormatter) {}

312

313

supportsType(type: BaseType): boolean {

314

return type instanceof MyComplexType;

315

}

316

317

getDefinition(type: MyComplexType): Definition {

318

// Use child formatter for nested types

319

const childDef = this.childTypeFormatter.getDefinition(type.getChildType());

320

321

return {

322

type: "object",

323

properties: {

324

value: childDef,

325

metadata: { type: "object" }

326

}

327

};

328

}

329

330

getChildren(type: MyComplexType): BaseType[] {

331

// Return child types that need their own definitions

332

return this.childTypeFormatter.getChildren(type.getChildType());

333

}

334

}

335

```

336

337

**Validation-Specific Formatter:**

338

339

```typescript

340

export class ValidationFormatter implements SubTypeFormatter {

341

supportsType(type: BaseType): boolean {

342

return type instanceof StringType;

343

}

344

345

getDefinition(type: StringType): Definition {

346

const definition: Definition = { type: "string" };

347

348

// Add custom validation rules

349

if (type.getId().includes("email")) {

350

definition.format = "email";

351

}

352

353

if (type.getId().includes("password")) {

354

definition.minLength = 8;

355

definition.pattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)";

356

}

357

358

return definition;

359

}

360

361

getChildren(type: StringType): BaseType[] {

362

return [];

363

}

364

}

365

```

366

367

### Error Handling in Extensions

368

369

Proper error handling for custom parsers and formatters.

370

371

```typescript

372

import { UnhandledError } from "ts-json-schema-generator";

373

374

export class SafeCustomParser implements SubNodeParser {

375

supportsNode(node: ts.Node): boolean {

376

return node.kind === ts.SyntaxKind.CustomSyntax;

377

}

378

379

createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType {

380

try {

381

// Custom parsing logic

382

return this.parseCustomNode(node, context);

383

} catch (error) {

384

throw new UnhandledError(

385

"Failed to parse custom node",

386

node,

387

error

388

);

389

}

390

}

391

392

private parseCustomNode(node: ts.Node, context: Context): BaseType {

393

// Implementation details

394

throw new Error("Not implemented");

395

}

396

}

397

```

398

399

### Testing Custom Extensions

400

401

Unit testing patterns for custom parsers and formatters.

402

403

```typescript

404

// test-custom-extensions.ts

405

import { expect } from "chai";

406

import ts from "typescript";

407

import { Context } from "ts-json-schema-generator";

408

import { CustomDateParser, DateType } from "./custom-date-parser.js";

409

410

describe("CustomDateParser", () => {

411

let parser: CustomDateParser;

412

413

beforeEach(() => {

414

parser = new CustomDateParser();

415

});

416

417

it("should support Date type references", () => {

418

const source = "let date: Date;";

419

const sourceFile = ts.createSourceFile("test.ts", source, ts.ScriptTarget.Latest);

420

const dateNode = /* extract Date reference node */;

421

422

expect(parser.supportsNode(dateNode)).to.be.true;

423

});

424

425

it("should create DateType for Date references", () => {

426

const dateNode = /* create Date reference node */;

427

const context = new Context();

428

const result = parser.createType(dateNode, context);

429

430

expect(result).to.be.instanceOf(DateType);

431

});

432

});

433

```

434

435

### Extension Distribution

436

437

Package and distribute custom extensions for reuse.

438

439

```json

440

// package.json for extension package

441

{

442

"name": "ts-json-schema-generator-date-extension",

443

"version": "1.0.0",

444

"main": "dist/index.js",

445

"types": "dist/index.d.ts",

446

"peerDependencies": {

447

"ts-json-schema-generator": "^2.0.0"

448

}

449

}

450

```

451

452

```typescript

453

// index.ts - extension package entry point

454

export { CustomDateParser, DateType } from "./custom-date-parser.js";

455

export { CustomDateFormatter } from "./custom-date-formatter.js";

456

457

// Convenience function for common usage

458

export function createDateExtension() {

459

return {

460

parser: new CustomDateParser(),

461

formatter: new CustomDateFormatter()

462

};

463

}

464

```