coffee-to-ts
v1.0.0
Published
Fast CoffeeScript to TypeScript converter with automatic type annotations
Maintainers
Readme
coffee-to-ts
Fast and effective CoffeeScript to TypeScript converter with automatic type annotations.
Features
- ✅ Type Annotations - Adds TypeScript type annotations to all functions and parameters
- ✅ AST-Based Transformation - Uses Babel for robust code transformations
- ✅ Import Modernization - Converts
require()to ES6importstatements - ✅ Code Modernization -
let→const, template literals, Number methods - ✅ ESLint Formatting - Applies proper code formatting with padding rules
- ✅ Safe - Creates backups, validates at each step
- ✅ Fast - Single-pass transformation (10-15x faster than iterative approaches)
- ✅ Verbose Mode - See exactly what's happening
Installation
npm install --save-dev coffee-to-tsOr use directly with npx:
npx coffee-to-ts src/file.coffeeUsage
CLI
# Basic usage
coffee-to-ts src/file.coffee
# Verbose mode
coffee-to-ts src/file.coffee --verbose
# Skip backup
coffee-to-ts src/file.coffee --no-backup
# Keep intermediate .js file
coffee-to-ts src/file.coffee --keep-intermediate
# Multiple files at once
coffee-to-ts src/**/*.coffeeProgrammatic API
import { convertFile } from 'coffee-to-ts'
const result = await convertFile('src/file.coffee', {
backup: true,
verbose: false,
keepIntermediate: false
})
console.log(`Output: ${result.outputFile}`)
console.log(`Types added: ${result.typesAdded}`)
console.log(`Imports converted: ${result.importsConverted}`)How It Works
3-Stage Pipeline
Stage 1: Decaffeinate
- Uses
decaffeinateto convert CoffeeScript → JavaScript - Removes decaffeinate suggestion comments
Stage 2: TypeScript Transformation
- Parses JavaScript to AST using
@babel/parser - Adds type annotations to:
- Function parameters (with callback pattern detection)
- Return types
- Class properties
- Converts
require()→ ES6import - Modernizes code patterns
- Single-pass, all in-memory (very fast!)
Stage 3: Format & Validate
- Applies ESLint auto-fix (using bundled config)
- Validates with TypeScript compiler
- Reports any remaining issues
Type Annotation Strategy
The tool uses a pragmatic approach to typing:
- Callback patterns:
(err: Error | null, data?: any) => void - Error parameters:
Error | null - db.open callbacks: Special handling for
(err, models, dbClose)pattern - All other parameters:
: any(safe default) - Return types: Inferred from simple literals, else
: any
This provides a working TypeScript baseline that you can refine with domain-specific types.
Configuration
Uses bundled ESLint config with sensible defaults including:
prefer-const- Convertslettoconstwhere appropriatepadding-line-between-statements- Blank lines after imports, before exports, between functions- Standard formatting rules
You can apply your own ESLint/Prettier configs after conversion.
Options
interface ConversionOptions {
backup?: boolean // Create .coffee.backup (default: true)
verbose?: boolean // Show detailed output (default: false)
keepIntermediate?: boolean // Keep intermediate .js file (default: false)
}Examples
Before & After
Input (CoffeeScript):
# user.coffee
class User
constructor: (@name, @email) ->
@createdAt = new Date()
greet: (callback) ->
setTimeout =>
callback null, "Hello, #{@name}!"
, 100
module.exports = UserOutput (TypeScript):
// user.ts
class User {
name: any
email: any
createdAt: any
constructor(name: any, email: any) {
this.name = name
this.email = email
this.createdAt = new Date()
}
greet(callback: (err: Error | null, data?: any) => void): void {
setTimeout(() => {
callback(null, `Hello, ${this.name}!`)
}, 100)
}
}
export default UserCLI Examples
Simple File
$ coffee-to-ts src/constants.coffee
✅ Conversion Complete
📁 Output: src/constants.ts
✨ Added 15 type annotations
📦 Converted 3 require() to import
✅ Applied ESLint formattingComplex File with Verbose Output
$ coffee-to-ts src/user-service.coffee --verbose
Stage 1: Decaffeinating...
Stage 2: Transforming to TypeScript...
Stage 3: Formatting and validation...
✅ Conversion Complete
📁 Output: src/user-service.ts
✨ Added 42 type annotations
📦 Converted 5 require() to import
✅ Applied ESLint formatting
⚠️ TypeScript: Some type errors remain (refine types as needed)Integration
In package.json scripts
{
"scripts": {
"convert": "coffee-to-ts",
"convert:verbose": "coffee-to-ts --verbose"
}
}With other tools
# Convert then test
coffee-to-ts src/file.coffee && npm test
# Convert multiple files
find src -name "*.coffee" -exec coffee-to-ts {} \;
# Convert with custom ESLint fix after
coffee-to-ts src/file.coffee && npx eslint src/file.ts --fixWhat It Fixes Automatically
✅ Type Annotations
- Function parameters:
: anyor specific patterns - Callback parameters:
(err: Error | null, data?: any) => void - Return types: inferred or
: any - Class properties:
: any
✅ Code Modernization
require()→ ES6importlet→constwhere not reassignedparseInt()→Number.parseInt()- Removes unnecessary
Array.from()
✅ Formatting
- Blank lines after imports
- Blank lines before exports
- Blank lines between functions
- Consistent indentation and quotes
❌ Requires Manual Refinement
- Domain-specific types (e.g.,
any→User,string→ specific unions) - Complex return types
- Generic type parameters
- Callback → async/await conversions
Why coffee-to-ts?
- Fast: Single-pass transformation is 10-15x faster than iterative approaches
- Pragmatic: Uses
anytypes as a safe baseline - refine them gradually - Battle-tested: Successfully converts complex real-world CoffeeScript codebases
- No config needed: Bundled ESLint config works out of the box
- Safe: Creates backups and validates at each step
Limitations
- Manual refinement needed: Generated
anytypes should be replaced with specific types - Complex patterns: Some advanced CoffeeScript patterns may need manual adjustment
- Not a compiler: This is a conversion tool, not a runtime transpiler
Troubleshooting
TypeScript errors after conversion
This is expected! The tool generates working TypeScript with any types. Gradually refine types:
// Generated
function getUser(id: any): any { ... }
// Refined
function getUser(id: string): Promise<User> { ... }ESLint errors
Apply your own ESLint config after conversion:
npx eslint src/**/*.ts --fixImport errors
Update imports in files that depend on converted modules:
// Before
const User = require('./user')
// After
import User from './user'Related Projects
- decaffeinate - CoffeeScript to JavaScript converter (used internally)
- lebab - ES5 to ES6+ transformation
License
MIT © Coston Perkins
See LICENSE file for details.
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
Quick Start for Contributors
git clone https://github.com/coston/coffee-to-ts.git
cd coffee-to-ts
npm install
npm run build
npm testChangelog
See CHANGELOG.md for version history.
