-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
213: ts-node for Running TypeScript Directly
I was working on a small utility script the other day—a shipping cost calculator that takes a package weight and a destination zone to determine the final price. It's not a full application, just a logic script I wanted to verify before integrating it into a larger project. Here is the code I had in shipping.ts:
interface ShippingRate {
zone: string;
basePrice: number;
perKg: number;
}
const rates: Record<string, ShippingRate> = {
'US-EAST': { zone: 'US-EAST', basePrice: 5, perKg: 2 },
'EU-WEST': { zone: 'EU-WEST', basePrice: 15, perKg: 5 },
};
function calculateCost(zone: string, weight: number): number {
const rate = rates[zone];
if (!rate) throw new Error('Invalid zone');
return rate.basePrice + (rate.perKg * weight);
}
console.log(`Total cost: $${calculateCost('US-EAST', 2.5)}`);
The Friction of the Compile Cycle
Naturally, my first instinct was to just run it. I hit the terminal and typed node shipping.ts. Predictably, Node screamed at me. It hit the interface keyword and immediately threw a SyntaxError. Node doesn't speak TypeScript; it only speaks JavaScript.
So, I did the "standard" dance. I ran npx tsc shipping.ts, which generated a shipping.js file. Then I ran node shipping.js. It worked perfectly. But then I realized I needed to change the basePrice for the EU-WEST zone. Now I'm stuck in this loop: edit the TS file, run the compiler, run the JS file, and then—because I'm a neat freak—delete the generated JS file so my folder doesn't get cluttered with build artifacts. It's a lot of overhead for a 15-line script.
Cutting Out the Middleman
This is where ts-node comes in. Instead of the two-step "compile then execute" process, ts-node lets us treat TypeScript files as if they were native to Node. I tried running it using npx to avoid a global installation:
npx ts-node shipping.ts
And just like that, the output appeared: Total cost: $10. No .js file was created in my directory. It felt like magic, but it's actually just ts-node hooking into the Node.js registration process. It compiles the TypeScript code in memory on the fly and hands the resulting JavaScript straight to the Node engine. For a script like this, the difference in execution speed is imperceptible, but the difference in developer experience is massive.
The Config Handshake
While playing around, I noticed something. I had a tsconfig.json in my project that set target to ESNext. I wondered if ts-node was just ignoring my settings and using its own defaults. To test this, I added a console.log using a very modern JS feature that older Node versions don't support.
I discovered that ts-node is actually quite respectful. It looks for your tsconfig.json and uses those settings to perform the in-memory transformation. If you have strict type-checking enabled in your config, ts-node will actually throw type errors in your terminal before it even tries to run the code. I actually prefer this—it's like having a miniature CI pipeline running every time I hit enter.
One word of caution, though: don't use this in production. Because ts-node has to compile the code every time the process starts, it's significantly slower to boot and uses more memory than running pre-compiled JavaScript. It's a development tool, a "scratchpad" for your logic, not a deployment strategy.
📋 Practical Task
Build a Currency Conversion CLI Tool
Create a TypeScript file named converter.ts that performs the following:
- Defines a
ConversionRatetype (or interface) that maps a currency code (like 'EUR' or 'GBP') to a number. - Implements a function
convert(amount: number, currency: string)that multiplies the amount by the rate from a predefined lookup object. - Logs the result of a conversion to the console.
Instead of using tsc to compile the file to JavaScript, use ts-node to execute the script directly from your terminal. Verify that the script runs and outputs the correct converted value without leaving any .js files in your folder.
There are no comments for now.