This guide will walk you through installing decoders, creating your first decoder, and validating data.
Installation
Install decoders using your preferred package manager:
Make sure you have "strict": true in your tsconfig.json for proper type inference.
Your first decoder
Let’s create a simple decoder to validate user data.
Import decoder functions
Start by importing the decoder functions you need: import { object , string , number , optional } from 'decoders' ;
These functions are the building blocks for creating decoders.
Define your decoder
Create a decoder that describes the structure of your data: const userDecoder = object ({
id: number ,
name: string ,
email: optional ( string ),
});
This decoder expects an object with:
id - a number (required)
name - a string (required)
email - an optional string
Validate data
Use .verify() to validate data and get a type-safe result: const externalData = {
id: 123 ,
name: 'Alice' ,
email: '[email protected] ' ,
};
const user = userDecoder . verify ( externalData );
// TypeScript knows: { id: number; name: string; email?: string }
console . log ( user . name ); // 'Alice'
If validation fails, .verify() throws an error with a detailed message.
Handle validation errors
For non-throwing validation, use .decode() to get a result object: const result = userDecoder . decode ( externalData );
if ( result . ok ) {
console . log ( 'Valid user:' , result . value );
} else {
console . error ( 'Validation failed:' , result . error );
}
The result is a discriminated union that you can check with result.ok.
Complete example
Here’s a complete working example you can run:
import { object , string , number , optional , array } from 'decoders' ;
// Define a decoder for user objects
const userDecoder = object ({
id: number ,
name: string ,
email: optional ( string ),
age: optional ( number ),
roles: array ( string ),
});
// Some data that needs validation (from API, user input, etc.)
const externalData = {
id: 123 ,
name: 'Alice Roberts' ,
email: '[email protected] ' ,
roles: [ 'admin' , 'user' ],
};
// Validate and use the data
try {
const user = userDecoder . verify ( externalData );
console . log ( 'Valid user!' );
console . log ( 'Name:' , user . name );
console . log ( 'Roles:' , user . roles );
// TypeScript knows all the types correctly!
} catch ( error ) {
console . error ( 'Invalid user data:' , error );
}
Common patterns
Validating API responses
Use decoders to validate data from external APIs:
import { object , string , number , array } from 'decoders' ;
const postDecoder = object ({
id: number ,
title: string ,
body: string ,
userId: number ,
});
async function fetchPost ( id : number ) {
const response = await fetch ( `https://api.example.com/posts/ ${ id } ` );
const data = await response . json ();
// Validate the API response
return postDecoder . verify ( data );
}
// Now you have a type-safe post object
const post = await fetchPost ( 1 );
console . log ( post . title ); // TypeScript knows this is a string
Nested objects
Handle complex nested structures:
import { object , string , number , array , optional } from 'decoders' ;
const addressDecoder = object ({
street: string ,
city: string ,
zipCode: string ,
});
const companyDecoder = object ({
name: string ,
address: addressDecoder , // Nested decoder
employees: array (
object ({
id: number ,
name: string ,
role: string ,
})
),
});
const company = companyDecoder . verify ( externalData );
console . log ( company . address . city ); // Fully type-safe
Optional fields with defaults
Provide default values for optional fields:
import { object , string , number , optional } from 'decoders' ;
const configDecoder = object ({
host: string ,
port: optional ( number , 3000 ), // Default to 3000 if missing
debug: optional ( boolean , false ), // Default to false
});
const config = configDecoder . verify ({ host: 'localhost' });
console . log ( config . port ); // 3000 (default value)
console . log ( config . debug ); // false (default value)
Handling errors
When validation fails, you get detailed error messages:
import { object , string , number } from 'decoders' ;
const userDecoder = object ({
id: number ,
name: string ,
});
const invalidData = {
id: '123' , // Wrong type - should be number
name: 'Alice' ,
};
const result = userDecoder . decode ( invalidData );
if ( ! result . ok ) {
console . log ( result . error );
// Shows exactly what failed and where
}
Error messages include:
Which field failed validation
What type was expected vs. what was received
The path to the failed field in nested objects
Next steps
Now that you’ve created your first decoder, explore more features:
Core concepts Understand how decoders work under the hood
Basic decoders Learn about all the built-in primitive decoders
Object validation Deep dive into validating complex objects
Custom decoders Build your own decoders for custom types