
The most fitting answer would be: Why not?
It is normal for every new tool introduced into the software industry to be tested, discussed, and criticized by the community. Over time, that pushes people to take sides as supporters or detractors.
Within the JavaScript community, TypeScript was never going to be an exception. But the real question most readers want answered is much simpler: is it good or bad, what does it give me, and what does it take away? The answer depends on the needs of the project and on whether TypeScript solves a real problem or merely adds unnecessary complexity.
What is TypeScript?
Some people describe TypeScript as a programming language. From my point of view, that definition is not completely accurate, because the code still needs to be transpiled to JavaScript in order to run. I prefer the definition from its own creators: a superset of JavaScript that allows us, among other things, to add typing.
That matters even more when we look at the wider ecosystem. With Deno arriving, and Ryan Dahl himself acknowledging that not choosing a typed language for Node.js was a mistake, TypeScript became even more clearly established as a serious option for building maintainable software.
Why did it appear?
Anyone who has worked on large projects with many developers and a lot of JavaScript already knows that the absence of typing, combined with the flexibility of object shapes, is a double-edged sword.
On the one hand, it gives us freedom. On the other, it forces us to trust that everyone uses that freedom responsibly. A simple typo, a property with an unexpected name, or a misunderstood implicit structure can introduce bugs that are hard to track down.
TypeScript appears precisely as an answer to that. When we work with modelled data, we know which attributes a class or object has, that information is explicit in the code, and we no longer have to guess its composition.
Where should we use it?
It can be used in both backend and frontend projects. That matters because TypeScript is not just a whim. It answers a different need depending on the context.
In backend code it is especially useful because we operate in the most abstract layer of the software: domain, services, contracts, and integrations. There, typing helps us model rules, enforce design commitments, and make dependencies between parts of the system more explicit.
In frontend, a common objection is that the generated output may be heavier than code hand-written directly in JavaScript. That is a fair point, but it needs perspective. In my view, downloading a few extra kilobytes is a small cost if the trade-off is better productivity, clearer semantics, and protection against classes of errors we would otherwise need to validate manually.
Who maintains it?
The community behind a tool matters a lot. It tells us something about support, evolution, and confidence in the future of the project.
In TypeScript’s case, Microsoft is the main force behind it, but far from the only one. Frameworks and communities such as Angular, Ionic, and Vue have also embraced it. On top of that, there is a huge community maintaining types for thousands of libraries across the JavaScript ecosystem.
One clear example is DefinitelyTyped, the large repository where many of the most widely used type definitions are hosted.
Alternatives
TypeScript is not the only tool with this goal. There are others, such as PureScript and ReasonML. Comparing them in depth is outside the scope of this article, but it is worth remembering that there is more than one way to approach typing and semantics in JavaScript applications.
What does it give us?
One of the most important, and least discussed, benefits is semantics. When you focus on modelling the domain of an application, the semantics of the code become essential. Strong typing helps reinforce them.
Consider this simplified example:
const character = findCharacterById(id);
character.run();
That code assumes too much:
- That
idis a validstring. - That
findCharacterByIdactually returns a character. - That the character implements
run().
That works while we stay on the happy path, but it leaves too many gaps everywhere else.
Now consider a more explicit version:
type CharacterId = string & { readonly brand: unique symbol };
interface Character {
id: CharacterId;
}
interface Hero extends Character {
saveTheDay(): void;
}
function findHeroById(id: CharacterId): Hero {
// ...
}
Here the identifier stops being “any string”, the method contract forces the correct input type, and the return type communicates domain intent more clearly. When we add semantics, we reduce assumptions and avoid unnecessary manual type checks.
What does it take away?
Of course, working with types also has a cost. It reduces some flexibility, forces us to define interfaces, and requires those contracts to evolve together with the design. That can slow development down, especially at the beginning.
A common example appears when we want to wrap children in a React component to build something like a carousel:
type CarouselProps = {
children: React.ReactElement[];
};
function Carousel({ children }: CarouselProps) {
return <div>{children.map((child) => child)}</div>;
}
In plain JavaScript we would probably access this.props.children and call map without thinking too much about it. In TypeScript, React’s types force us to be explicit about what we actually expect. That adds work, but it also clarifies the component contract.
Bad uses: any
One of the most “fun” types in TypeScript is any, precisely because it accepts anything.
It can be useful in an early development stage, when you want to keep moving and you do not yet understand the contract of a library or integration. The problem comes when any stays forever. At that point we lose much of the value of TypeScript and keep most of the cost.
Bad uses: optional properties
Something similar happens with optional properties:
type Exercise = {
name?: string;
warning?: string;
};
The fact that something can be optional does not mean we should make the whole model optional. If we overuse that pattern, we fall back into the same problem we wanted to avoid: not being sure what exists and what does not.
Conclusion
From my point of view, TypeScript has a light learning curve and can produce transpiled output that is less optimal than a carefully hand-written JavaScript implementation. Even so, in most cases the gains in clarity, semantics, maintainability, and early error detection more than make up for that cost.
That does not mean we must always use it. If a project is very short-lived or we are only building a quick spike, it may be overengineering. The real value lies in knowing when a tool helps and when it gets in the way.
This is not about being against plain JavaScript, nor about claiming that developers who do not use TypeScript are wrong. It is about choosing well for the context at hand.