TypeScript: JavaScript With a Safety Net
In this episode of the Smashing Podcast, Drew McLellan sits down with Stefan Baumgartner, author of TypeScript in 50 Lessons, to unpack what TypeScript is and how it helps developers write more reliable JavaScript.
Why It Matters
TypeScript isn't a new language—it's JavaScript with type syntax layered on top. That means you get all the flexibility of plain JavaScript while gaining the ability to catch entire categories of bugs before your code ever runs.
The appeal isn't just about catching typos or wrong parameter types. It's about the confidence that comes from the compiler validating the contract at every function boundary and data handoff. Mismatched object shapes, null checks, and missed undefined returns become compiler errors instead of runtime surprises.
Better still, TypeScript doesn't demand a full rewrite. Because it's a superset, you can migrate file by file, turn the enforcement dial up or down, and still share the very same libraries and tooling JavaScript developers already rely on. The payoff is cumulative: the more the types cover, the better the editor suggestions and refactoring tools become.
Practical Applications
For frontend work, TypeScript earns its keep most when projects grow past a certain scale—once multiple developers are touching the same component trees or a codebase needs to survive longer than one release cycle. Enforced types turn the codebase into a living documentation that can't go stale, saving everyone on the team a lot of head-scratching.
Even side projects benefit: better autocomplete, safer find-and-replace operations, and bug fixes recommended by the type checker rather than discovered in the browser's console. That alone makes TypeScript a habit worth building.
Getting Started
If you want to dive deeper into TypeScript, Stefan Baumgartner's book TypeScript in 50 Lessons is out now, and the official documentation at typescriptlang.org offers a solid running start. You can follow Stefan on Twitter or check out his writing and speaking material on his personal site.
From the Archive
This week the magazine also published a round of strong reads on frontend topics worth checking out:
- “React Form Validation With Formik And Yup”, written by Nefe Emadamerho-Atori
- “Design Shopping: Get A Faster Client Buy-In Through A Guided Design Showcase”, written by Kelly Schummer
- “Build And Deploy An Angular Form With Netlify Forms And Edge”, written by Zara Cooper
- “Managing Long-Running Tasks In A React App With Web Workers”, written by Chidi Orji
- “Supercharge Testing React Applications With Wallaby.js”, written by Kelvin Omereshone
TypeScript as a Tooling Layer, Not a New Language
Stefan Baumgartner, web developer based in Linz, Austria, and author of TypeScript in 50 Lessons, joined the Smashing Podcast to discuss what TypeScript is and what problems it solves. Currently working at the web performance company Dynatrace, Baumgartner writes, speaks, and organizes events about software development and web technologies.
Baumgartner's preferred way to describe TypeScript is as a tooling layer on top of JavaScript. JavaScript has its quirks: dynamic typing means a variable's type—number, string, or object—depends on its position in the code, and there is lots of implicit knowledge about interfaces, APIs, and function signatures, especially when working with Node.js.
TypeScript attempts to provide a type system around all that, figuring out which types you set when assigning variables, which function signatures expect which values at which positions, and which return objects you can then access, modify, and work with. Crucially, every JavaScript code is valid TypeScript code, meaning if you know JavaScript, you are essentially a TypeScript developer already.
Strictness Is Optional, But Helpful
TypeScript imposes stricter rules only to the degree that you want them. The strictness is entirely up to you: you can tell TypeScript how strict to be. Its primary goal is to catch as many possible errors as it can—for example, warning that a value could be null or undefined, so you should check for its existence first.
This relates to the distinction between weakly (or dynamically) typed languages and strongly (or statically) typed ones. In a dynamically typed language like JavaScript, assigning a value of a different class or type to a variable is possible at any point. A variable might be a number early on and later be reassigned a string. This can lead to errors where a developer expects one type but gets another. For example, adding the string "2" to the number 2 yields the string "22", whereas swapping them yields the number 4. TypeScript prevents such errors by ensuring once a variable receives a type via assignment, that type does not change.
There are two ways to work with TypeScript. The first involves writing TypeScript that gets compiled down to regular JavaScript, where types are erased. TypeScript can also transpile down to older ECMAScript versions for compatibility. The second approach is to write standard JavaScript, add type declarations in a separate file, and use JSDoc comments to refer to them. TypeScript can read that documentation information and provide the same tooling benefits.
Tooling and Editors Are the Primary Benefit
The biggest advantage of TypeScript comes through code editors. Visual Studio Code, for instance, has TypeScript built in as a checker and analyzer that figures out as much information as possible from plain JavaScript and returns that to the editor. This feature isn't limited to VS Code; thanks to the language server protocol, TypeScript support is available in almost any modern editor that supports it.
For larger projects with a compile step, TypeScript also plays a role in continuous integration. With every commit to a repository, type checks can be run to catch errors that might have slipped in.
TypeScript is designed to infer as much type information from JavaScript as possible without any intervention. If it sees a number in the wild, it knows the type is number. Default values in function signatures tell TypeScript which types to expect. This inference covers many use cases. When it doesn't, that's where you add explicit type information, such as declaring compound object types like Article that have properties like name, description, and price.
One distinctive feature is structural typing. As long as the shape—properties and their types—of an object matches another object's shape, TypeScript considers them compatible. A type called book and a type called video with identical properties are mutually compatible even if declared with different names.
Modeling Data and Null Checks
For models like a cart with an array of products, TypeScript enforces the shape at every level. This includes generic types, such as Array<Article>, where generic constraints define inner values. When mapping over an array, the type system provides the inner type—string, number, or article—inside the callback.
Generics scale to more advanced behaviors. JavaScript functions can often mean many different things depending on argument types: if the first argument is a string, a second argument must be an object; if the first is a number, the second must be a string. TypeScript uses conditional types and generic structures to model such scenarios, which is where the type system becomes both powerful and admittedly mind boggling.
Type definitions typically live close to the code. A structural TypeScript setup has TypeScript files on one side and type definition files on the other. TypeScript joins the existing modern build toolchain that many are used to—whether Babel, JSX, or WebPack—either as the sole transpiler (except bundling, which relies on tools like Rollup or Webpack) or via an interface with Babel.
Type annotations apply to function expectations. You can declare the first argument as a number, second as a string, and the tooling catches mismatches. For values that may be null or undefined, TypeScript allows union types. You can state that an argument can be a string, a number, null, undefined, or an object type using the pipe operator. Adding | null forces checks for null-ish values inside the function, erasing a whole class of common errors. This often proves far more tedious than adding null checks throughout the code, but it's also a safer and refreshing way to handle null and undefined values.
Interfaces, Classes, and False Friends
TypeScript does offer interfaces and classes, but with significances different from languages like Java or C#. Those names can be false friends. TypeScript was created over eight years ago, when JavaScript was more primitive and lacked classes. The team initially introduced features such as classes, interfaces, extra classes, and namespaces, borrowed from other object-oriented languages. Yet as many of these concepts—especially classes—made their way into JavaScript over time, TypeScript realigned them to match standard JavaScript.
Now, TypeScript classes are the same as JavaScript classes, while TypeScript interfaces are merely composite type declarations. There is an implements keyword, but functioning in ways subtle to a Java or C# developer.
While TypeScript can support extremely advanced uses, it is also accessible. Its credibility lies in usage being gradual: beginners gain tooling like document.querySelector autocompletion with zero configuration, letting them learn without red-streak errors. As they progress, they can adopt more sophisticated type concepts incrementally. Advanced feature adoption is only needed where codebase requirements demand it. Baumgartner recalls how working with a React component and having control space list all the properties his function component expected was motivating enough to keep going deeper.
Library Adoption and Organizational Benefits
Frameworks writing in TypeScript—like the Vue 3 rewrite or Preact—offer users implicit documentation that includes function signatures, object shapes, and extra checks, essentially for free. Library contributions become smoother because contributors don't need to learn the intricate type system if the codebase follows a pattern of JavaScript with types on the side. This gives a low-entry barrier for open source participation while still gaining type-safe benefits.
Larger teams and organizations with developers migrating from Java or C# to JavaScript can use TypeScript as a guide through JavaScript's quirks, history, and limitations. Because it provides a familiar type layer, there are fewer surprises, though it doesn't solve communication problems directly. It guides discussions around intent and design rather than details like "what do you expect from this argument?" Revisiting code after months or years becomes easier with types, hinting at what you originally meant.
Beyond CoffeeScript Misconceptions
Common avoidances of TypeScript stem from comparisons with tools like CoffeeScript and preconceptions of JavaScript developers seeing it as Java for children. Baumgartner relates this closely, having avoided TypeScript for six years until hearing interviews with its creator Anders Hejlsberg. Hejlsberg emphasizes he is fundamentally writing JavaScript with added annotations—reaffirming TypeScript is not a wholly new language.
TypeScript stays aligned with the ECMAScript standard: they don't propose new JavaScript language features, only type system innovations. Innovations blend with actual code, but the concept of "JavaScript with benefits" holds. Union types and intersection types are Baumgartner's favorite type system features for their balance of power to comprehension. They allow arguments or variables to be one type or another, or amalgamations, better modeling application domains day to day.
Beyond type modeling, having one transpiler for TSX, plain TypeScript, or down-level ECMAScript simplifies toolchains, allowing a reduction in dependencies and noise.
Learning to Teach the Type System
TypeScript in 50 Lessons assumes readers are already JavaScript developers—need to know the basics, function, object, error, assignment—but not seasoned ones. The book's target is the type system itself and focuses on teaching its lasting principles instead of covering every feature. With four TypeScript releases a year, the author prioritized validity for years to come. Chapters are bite-sized, each taking about five to ten minutes, with code samples available online at TypeScript-Book.com.
The book bridges start to advanced with a watershed chapter on union and intersection types, followed by type modeling, before culminating in the most recent—conditional and generic—chapters available. Seven chapters total equip readers with sustainable knowledge for future TypeScript type additions.
Baumgartner shares that writing the book taught him two widely unexpected things. First: observing TypeScript's bundled type definitions for web standards (lib.d.ts files) is educational. They're generated from W3C's Web Interface Definition Language, structured to be accessible from ECMAScript 5 through 2021 standards and well documented. He was so engrossed he set aside days reading through them.
Second: doing deep dives into generics and conditional types mechanics makes their usage more intuitive. Constructing and reasoning about types becomes simpler understanding evaluation underneath—such as with step-by-step flows in his book from conditional type to result type—knowledge that grows joyfully more robust.
For those getting started, the TypeScript Playground is an interactive online editor filled with examples. Documentation infrastructure by Orta, who also wrote the book's foreword, complements the tailored and often opinionated material of the book as a large thinking resource.



