Skip to content

Enhance Union Type Error Details #181

@itizarsa

Description

@itizarsa

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

Problem

When using fastify-type-provider-typebox, union type validation failures provide minimal error details:

{ 
  message: 'Expected union value', 
  instancePath: '' 
}

This lacks the detailed validation information that TypeBox provides through its errors property in the ValueError interface, making it difficult for developers to debug union-type validation failures.

Background

TypeBox's ValueError interface contains rich error information:

export interface ValueError {
    type: ValueErrorType;
    schema: TSchema;
    path: string;
    value: unknown;
    message: string;
    errors: ValueErrorIterator[]; // Contains detailed validation results for each union member
}

Current error mapping in fastify-type-provider-typebox:

error: errors.map((error) => ({
  message: `${error.message}`,
  instancePath: error.path
}))

Proposal

Enhance the error mapping to include detailed union validation information:

import { TypeCompiler, ValueError, ValueErrorIterator } from '@sinclair/typebox/compiler';
import { Type } from '@sinclair/typebox';

const CreateActionDelayBody = Type.Object({
	action: Type.Literal('DELAY'),
	days: Type.Number(),
});

const CreateActionSMSBody = Type.Object({
	action: Type.Literal('SMS'),
	phone: Type.String(),
});

const CreateActionEmailBody = Type.Object({
	action: Type.Literal('EMAIL'),
	email: Type.String({ format: 'email' }),
});

const CreateActionBody = Type.Union([
	CreateActionDelayBody,
	CreateActionSMSBody,
	CreateActionEmailBody,
]);

const C = TypeCompiler.Compile(CreateActionBody);

type ReducedError = [{ message: string; path: string; errors: ReducedError[] }];

const MapValueIterator = (iterator: ValueErrorIterator): ReducedError[] =>
	[...iterator].map((error) => MapValueError(error)) as never;

const MapValueError = (error: ValueError) => ({
	message: error.message,
	path: error.path,
	errors: error.errors.map((iterator) => MapValueIterator(iterator)),
});

const value = { action: 'ONE' };

const R = MapValueIterator(C.Errors(value));

console.dir(R, { depth: 100 });

Example Output

[
	{
		"message": "Expected union value",
		"path": "",
		"errors": [
			[
				{
					"message": "Expected required property",
					"path": "/days",
					"errors": []
				},
				{ "message": "Expected 'DELAY'", "path": "/action", "errors": [] },
				{ "message": "Expected number", "path": "/days", "errors": [] }
			],
			[
				{
					"message": "Expected required property",
					"path": "/phone",
					"errors": []
				},
				{ "message": "Expected 'SMS'", "path": "/action", "errors": [] },
				{ "message": "Expected string", "path": "/phone", "errors": [] }
			],
			[
				{
					"message": "Expected required property",
					"path": "/email",
					"errors": []
				},
				{ "message": "Expected 'EMAIL'", "path": "/action", "errors": [] },
				{ "message": "Expected string", "path": "/email", "errors": [] }
			]
		]
	}
]

I understand we need to stick to FastifySchemaValidationError. I'm raising this here to discuss how to better support it.

Motivation

No response

Example

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions