ts-csv-parser
v1.0.1
Published
Utility package for parsing CSV files.
Maintainers
Readme
CSV Parser
A modern, type-safe CSV parser for TypeScript with zero dependencies. Built from scratch with a functional programming approach, featuring advanced type inference, flexible validation, and comprehensive error handling.
Features
- 🎯 Type-safe - Complete TypeScript support with automatic type inference
- 🔄 Transform & Validate - Built-in data transformation and validation pipeline
- 🚫 Zero Dependencies - No external dependencies, lightweight and secure
- 🛡️ Error Handling - Comprehensive error reporting with precise row/column information
- 🔧 Flexible - Support for custom delimiters, quotes, and parsing options
- 🏗️ Immutable - Functional programming approach with immutable parser instances
- 📱 Modern - ES modules, async support, and File API compatibility
Installation
npm install ts-csv-parser
# or
yarn add ts-csv-parserQuick Start
import { Parser } from 'ts-csv-parser';
const parser = new Parser()
.col('Name', 'name')
.col('Age', 'age', { transform: (v) => parseInt(v) })
.col('Email', 'email', { nullable: true });
const csv = `Name,Age,Email
John,30,[email protected]
Jane,25,
Bob,35,[email protected]`;
const result = parser.parse(csv);
console.log(result.success);
// [
// { name: 'John', age: 30, email: '[email protected]' },
// { name: 'Jane', age: 25, email: null },
// { name: 'Bob', age: 35, email: '[email protected]' }
// ]Parser Options
Configure global parsing behavior:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| delimiter | string | ',' | Field separator (supports multi-character) |
| quote | string | '"' | Quote character |
| escape | string | '"' | Escape character |
| skipEmptyLines | boolean | true | Skip empty lines |
| skipRows | number | 0 | Number of rows to skip at start |
| trim | boolean | true | Trim whitespace from fields |
| caseInsensitiveColumnNames | boolean | false | Case-insensitive column matching |
const parser = new Parser({
delimiter: ';',
skipRows: 1,
caseInsensitiveColumnNames: true
});Column Definition
Basic Column Mapping
const parser = new Parser()
.col('First Name', 'firstName') // CSV column → object property
.col('Last Name', 'lastName');Column Aliases
Map multiple possible column names to the same property:
const parser = new Parser()
.col(['Name', 'Full Name', 'Customer Name'], 'name')
.col(['Email', 'Email Address'], 'email');Type Transformations
Transform string values to other types:
const parser = new Parser()
.col('Age', 'age', { transform: (v) => parseInt(v) })
.col('Price', 'price', { transform: (v) => parseFloat(v) })
.col('Active', 'active', { transform: (v) => v.toLowerCase() === 'true' })
.col('Tags', 'tags', { transform: (v) => v.split(',').map(s => s.trim()) });
// Resulting type: { age: number, price: number, active: boolean, tags: string[] }Nullable Fields
Handle optional/missing values:
const parser = new Parser()
.col('Name', 'name') // Required field
.col('Email', 'email', { nullable: true }) // Optional field
.col('Phone', 'phone', { nullable: true }); // Optional field
// Type: { name: string, email: string | null, phone: string | null }Default Values
Provide fallback values for missing fields:
const parser = new Parser()
.col('Name', 'name')
.col('Role', 'role', { defaultValue: 'user' })
.col('Score', 'score', {
transform: (v) => parseInt(v),
defaultValue: 0
});Validation
Column Validation
Validate individual field values after transformation:
const parser = new Parser()
.col('Email', 'email', {
validate: (v) => v.includes('@') ? undefined : 'Invalid email format'
})
.col('Age', 'age', {
transform: (v) => parseInt(v),
validate: (v) => v >= 0 && v <= 120 ? undefined : 'Age must be between 0 and 120'
});Row Validation
Validate entire rows with cross-field validation:
const parser = new Parser()
.col('StartDate', 'startDate', { transform: (v) => new Date(v) })
.col('EndDate', 'endDate', { transform: (v) => new Date(v) })
.val((row) => {
if (row.endDate <= row.startDate) {
return 'End date must be after start date';
}
return undefined; // Valid
});Complex Business Rules
const parser = new Parser()
.col('Price', 'price', { transform: (v) => parseFloat(v) })
.col('Quantity', 'quantity', { transform: (v) => parseInt(v) })
.col('Total', 'total', { transform: (v) => parseFloat(v) })
.val((row) => {
const expectedTotal = row.price * row.quantity;
if (Math.abs(expectedTotal - row.total) > 0.01) {
return `Total calculation error: expected ${expectedTotal.toFixed(2)}, got ${row.total}`;
}
return undefined;
});Error Handling
The parser provides detailed error information for debugging:
const result = parser.parse(csv);
if (result.hasErrors) {
result.errors.forEach(error => {
console.log(`Row ${error.row}: ${error.message}`);
if (error.type !== 'row-validation') {
console.log(` Column: ${error.column}`);
console.log(` Property: ${error.property}`);
console.log(` Value: ${error.value}`);
}
});
}Error Types
| Type | Description |
|------|-------------|
| transform | Error during data transformation |
| validation | Field validation failure |
| missing | Required column not found |
| row-validation | Row-level validation failure |
⚠️ Row Numbers: Error row numbers correspond to actual CSV file rows (including header), making it easy to locate issues in spreadsheet applications.
Advanced Features
Case-Insensitive Column Matching
// Global setting
const parser = new Parser({ caseInsensitiveColumnNames: true })
.col('NAME', 'name') // Matches "name", "Name", "NAME", etc.
// Per-column setting
const parser2 = new Parser()
.col('NAME', 'name', { caseInsensitiveColumnNames: true });Multi-Character Delimiters
const parser = new Parser({ delimiter: '::' })
.col('Name', 'name')
.col('Value', 'value');
const csv = 'Name::Value\nJohn::123\nJane::456';Custom Quote and Escape Characters
const parser = new Parser({
delimiter: '|',
quote: "'",
escape: '\\'
});Async File Parsing
// Parse File objects (browser)
const fileInput = document.getElementById('csv-file') as HTMLInputElement;
const file = fileInput.files[0];
const result = await parser.parseAsync(file);
// Parse strings asynchronously
const result2 = await parser.parseAsync(csvString);Type Inference
The parser automatically infers result types based on your column definitions:
const parser = new Parser()
.col('Name', 'name') // string
.col('Age', 'age', { transform: (v) => parseInt(v) }) // number
.col('Active', 'active', { transform: (v) => v === 'true' }) // boolean
.col('Email', 'email', { nullable: true }) // string | null
.col('Score', 'score', {
transform: (v) => parseInt(v),
defaultValue: 0
}); // number
// Inferred type:
// {
// name: string;
// age: number;
// active: boolean;
// email: string | null;
// score: number;
// }💡 Tip: The parser uses TypeScript's advanced type system to provide compile-time type safety while maintaining runtime flexibility.
Complete Example
import { Parser } from 'ts-csv-parser';
// Employee data parser with comprehensive validation
const employeeParser = new Parser({ delimiter: ',' })
.col('Employee ID', 'id', {
validate: (v) => /^EMP\d{3}$/.test(v) ? undefined : 'ID must match pattern EMP###'
})
.col(['Full Name', 'Name'], 'name', {
validate: (v) => v.length >= 2 ? undefined : 'Name must be at least 2 characters'
})
.col('Email', 'email', {
validate: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) ? undefined : 'Invalid email'
})
.col('Salary', 'salary', {
transform: (v) => parseFloat(v),
validate: (v) => v > 0 ? undefined : 'Salary must be positive'
})
.col('Start Date', 'startDate', {
transform: (v) => new Date(v),
validate: (d) => !isNaN(d.getTime()) ? undefined : 'Invalid date format'
})
.col('Department', 'department', {
validate: (v) => ['HR', 'Engineering', 'Sales', 'Marketing'].includes(v)
? undefined : 'Invalid department'
})
.col('Manager', 'manager', { nullable: true })
.val((employee) => {
// Cross-field validation
if (employee.department === 'Engineering' && employee.salary < 50000) {
return 'Engineering salaries must be at least $50,000';
}
return undefined;
});
const csv = `Employee ID,Full Name,Email,Salary,Start Date,Department,Manager
EMP001,John Smith,[email protected],75000,2023-01-15,Engineering,
EMP002,Jane Doe,[email protected],65000,2023-02-01,Engineering,John Smith
INVALID,Bob,invalid-email,30000,bad-date,InvalidDept,`;
const result = employeeParser.parse(csv);
console.log('Successful records:', result.success.length);
console.log('Errors:', result.errors.length);
result.errors.forEach(error => {
console.log(`Row ${error.row}: ${error.message}`);
});License
MIT
