
TypeScript
First appeared 2012 · Anders Hejlsberg
JavaScript with a type system bolted on top — all the reach, far fewer runtime surprises.
Overview
TypeScript is a strongly typed, object-oriented programming language that builds on JavaScript, giving you better tooling, error detection, and code quality at any scale. Developed and maintained by Microsoft since 2012, TypeScript is a syntactic superset of JavaScript, meaning that any valid JavaScript code is also valid TypeScript code. The language adds optional static type definitions to JavaScript, enabling developers to catch errors early during development through a sophisticated type system, making JavaScript development more efficient, maintainable, and scalable. TypeScript code compiles down to clean, readable, standards-compliant JavaScript that runs anywhere JavaScript runs: in web browsers, on Node.js servers, in mobile applications, or in any JavaScript engine. It fully supports all modern JavaScript features from ECMAScript standards while providing additional powerful capabilities like interfaces for defining contracts, generics for reusable type-safe code, enums for named constants, decorators for meta-programming, advanced type inference that understands complex patterns, union and intersection types, type guards, and much more. TypeScript's type system is structural rather than nominal, meaning types are compatible based on their structure rather than explicit declarations, which aligns well with JavaScript's dynamic nature. The language provides excellent IDE support with intelligent code completion, refactoring tools, inline documentation, and real-time error detection, dramatically improving developer productivity and reducing bugs. TypeScript is particularly valuable for large-scale applications, team collaboration, and long-term code maintenance where type safety, clear interfaces, and self-documenting code are crucial. It has become the de facto standard for enterprise JavaScript development and is widely adopted across the industry, from startups to Fortune 500 companies. TypeScript's gradual typing system allows developers to adopt it incrementally, adding types to existing JavaScript codebases at their own pace without requiring a complete rewrite. The language has a vibrant ecosystem with extensive community support, comprehensive documentation, and integration with virtually every major JavaScript framework, library, and build tool. TypeScript's influence extends beyond just adding types; it has shaped modern JavaScript development practices, influenced ECMAScript proposals, and set standards for how large-scale JavaScript applications should be structured and maintained.
Key facts
The reference details, without the paragraph.
- First appeared
- 2012
- Designed by
- Anders Hejlsberg at Microsoft (also behind Turbo Pascal, Delphi and C#)
- Typing
- Static, structural and gradual — types are checked at compile time and erased at runtime
- Execution
- Compiles to plain JavaScript; runs anywhere JavaScript runs
- Relationship to JavaScript
- A strict superset — every valid `.js` file is a valid `.ts` file
- Package manager
- npm, pnpm, yarn or bun
- File extensions
- .ts, .tsx, .d.ts
- Release cadence
- A new minor version roughly every three months
- Licence
- Apache 2.0
History
How the language got here — the decisions that still shape how you write it.
TypeScript was developed by Microsoft and first released in October 2012, created by Anders Hejlsberg, the renowned language designer who also created C#, Delphi, and Turbo Pascal. The motivation behind TypeScript emerged from Microsoft's own struggles with building and maintaining large-scale JavaScript applications, particularly for projects like Office 365 and Visual Studio Online. The lack of static typing in JavaScript made code maintenance, refactoring, and collaboration across large teams extremely challenging, leading to runtime errors that could have been caught at compile time. Microsoft recognized that while JavaScript was ubiquitous and powerful, it lacked the tooling support and type safety that developers needed for enterprise-scale applications. TypeScript was designed as a pragmatic solution that would provide the benefits of static typing while maintaining full backward compatibility with existing JavaScript code and the entire JavaScript ecosystem. The language was open-sourced from the beginning, with development happening publicly on GitHub, which helped build community trust and adoption. TypeScript's design philosophy emphasized gradual adoption, allowing developers to add types incrementally to existing JavaScript codebases without requiring a complete rewrite. The first major breakthrough came in 2014 when the Angular team at Google announced that Angular 2 would be built with TypeScript, providing significant validation and visibility for the language. This decision was driven by Angular's need for better tooling, maintainability, and developer experience for large applications. Following Angular's adoption, other major frameworks and libraries began embracing TypeScript, including React (with extensive TypeScript support), Vue 3 (rewritten in TypeScript), and countless others. The language gained momentum in the open-source community, with major projects like Visual Studio Code (itself written in TypeScript), Slack's desktop application, Asana, and Airbnb's frontend infrastructure adopting it. TypeScript's type system has evolved significantly over the years, introducing advanced features like conditional types, mapped types, template literal types, and sophisticated type inference that can understand complex JavaScript patterns. The language has maintained its commitment to tracking ECMAScript standards, ensuring that every new JavaScript feature is supported in TypeScript, often with enhanced type safety. Microsoft's investment in TypeScript has been substantial, with a dedicated team continuously improving the compiler's performance, error messages, and type-checking capabilities. The TypeScript compiler has become remarkably fast, capable of type-checking millions of lines of code efficiently. The language's impact extends beyond just adding types to JavaScript; it has influenced how developers think about code organization, API design, and software architecture. TypeScript's success has inspired similar efforts in other dynamic languages and has become a model for how to add static typing to an existing language ecosystem. Today, TypeScript is one of the most loved and widely used programming languages, consistently ranking high in developer satisfaction surveys. Its adoption continues to grow across startups, enterprises, and open-source projects, cementing its position as an essential tool for modern web development. The language continues to evolve with regular releases every few months, adding new features, improving type inference, and maintaining compatibility with the ever-evolving JavaScript ecosystem.
- 2012
Microsoft announces TypeScript
Anders Hejlsberg presents a typed superset of JavaScript aimed at the problem Microsoft had internally: large JavaScript applications that no one could safely refactor.
- 2013
DefinitelyTyped
A community repository of type definitions for existing JavaScript libraries launches. It solves the bootstrapping problem — you can adopt TypeScript without waiting for every dependency to convert.
- 2016
Angular 2 adopts it as the default
A major framework choosing TypeScript as its primary language moves it from a Microsoft project to an ecosystem standard.
- 2018
Types get powerful
Conditional types, mapped types and `infer` arrive. The type system becomes expressive enough to model real JavaScript APIs rather than approximating them.
- 2020
Template literal types
Types can be computed from string literals, enabling precisely typed routes, event names and CSS units — a level of precision most static languages do not offer.
- 2023
`satisfies` and const type parameters
`satisfies` lets you check a value against a type without widening it, closing a long-standing gap between inference and validation.
- 2025
A native compiler
Microsoft begins porting the compiler to Go for a roughly tenfold speed-up on large codebases, addressing the single most common complaint about TypeScript at scale.
What it is good at
The reasons teams pick it, stated concretely.
Refactoring stops being frightening
Rename a field, change a signature, or delete a branch and the compiler lists every site that must change. On a codebase of any size this is the single largest productivity difference from plain JavaScript.
Your editor becomes genuinely useful
Accurate autocomplete, inline documentation, go-to-definition and find-all-references all fall out of having types. Much of the value arrives before you have written a single annotation, through inference.
Structural typing fits JavaScript's habits
Types match on shape, not on declared inheritance. An object literal with the right fields satisfies an interface without announcing it, so typing existing JavaScript patterns rarely requires restructuring them.
Gradual adoption is real
Rename a file to `.ts`, leave `strict` off, and fix errors at your own pace. `any` is an escape hatch that lets a migration proceed incrementally instead of as a big-bang rewrite.
Types shared across the whole stack
One definition of a request payload can be enforced in the API handler, the client that calls it and the tests for both. Tools such as tRPC and Zod push this further, deriving runtime validation and types from a single source.
Trade-offs
Every language costs you something. Knowing what, before you commit, is the whole point.
Types vanish at runtime
The compiler erases everything. Data crossing a boundary — a network response, `JSON.parse`, a form — is unchecked at runtime no matter how precisely it is typed. Validate at the edges with something like Zod or Valibot.
A build step you cannot skip
Plain JavaScript runs as written. TypeScript needs compilation, source maps and configuration. Newer runtimes reduce the friction by executing `.ts` directly, but the type-check is still a separate job.
The type system has a deep end
Conditional types, mapped types and recursive generics can express remarkable things and produce error messages dozens of lines long. Knowing when to stop and write `as` is a skill in itself.
Compile times on large codebases
A million-line project can take minutes to type-check. Project references and `skipLibCheck` help, and the native compiler port is aimed squarely at this.
`any` quietly disables everything
One `any` propagates through every expression that touches it, silently switching off checking in places you did not intend. Prefer `unknown` and narrow explicitly.
Code examples
Not syntax tours — the idioms that make code read like the language rather than a translation of another one.
type RequestState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: User[] }
| { status: 'error'; message: string };
function render(state: RequestState): string {
switch (state.status) {
case 'idle': return 'Nothing requested yet';
case 'loading': return 'Loading…';
case 'success': return `${state.data.length} users`; // data exists here
case 'error': return state.message; // and message here
}
}import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
email: z.string().email(),
role: z.enum(['admin', 'member']),
});
// One definition produces both the runtime check and the static type.
type User = z.infer<typeof UserSchema>;
async function loadUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return UserSchema.parse(await response.json()); // throws on bad shape
}function pick<T extends object, K extends keyof T>(source: T, keys: K[]): Pick<T, K> {
const result = {} as Pick<T, K>;
for (const key of keys) result[key] = source[key];
return result;
}
const user = { id: 1, name: 'Ada', email: 'ada@example.com', password: 'secret' };
const safe = pick(user, ['id', 'name']);
safe.name; // string — known
// safe.password // compile error: property does not existfunction parseConfig(raw: unknown): { port: number } {
if (typeof raw !== 'object' || raw === null) {
throw new Error('config must be an object');
}
if (!('port' in raw) || typeof raw.port !== 'number') {
throw new Error('config.port must be a number');
}
return { port: raw.port }; // narrowed to number by the checks above
}Common pitfalls
The mistakes that cost everyone an afternoon at least once.
Believing types exist at runtime
You cannot check a type with `if`, and a cast does not convert anything. `as User` is a promise you make to the compiler, not a verification — if the data is wrong, it stays wrong.
Reaching for `as` to silence errors
An assertion turns a compile error into a runtime crash later. When a type does not fit, the error is usually correct; fix the shape or narrow properly instead.
Running with `strict` disabled
Without `strictNullChecks`, `undefined` is assignable to everything and the compiler cannot catch the most common bug in JavaScript. New projects should start strict; existing ones should migrate one flag at a time.
`enum` where a union would do
TypeScript enums generate runtime code and have surprising numeric behaviour. A union of string literals is simpler, erasable and usually a better fit.
Typing what inference already knows
`const count: number = 0` adds noise without adding information. Annotate function parameters, return types at module boundaries, and little else.
Trusting `JSON.parse`
It returns `any`, so everything downstream silently loses checking. Type it as `unknown` and validate, or parse through a schema.
In production
Where it is running at scale, and what it is doing there.
Microsoft
Office 365, Visual Studio Code, and various web applications.
Slack
Desktop and web applications for better type safety.
Airbnb
Frontend applications and internal tools.
Asana
Web application development with improved developer experience.
Learning path
A realistic order to learn things in, with something to build at each step.
- 1
Prerequisite
Know JavaScript first
Closures, `this`, promises and the module system. TypeScript adds types to JavaScript semantics; if the semantics are shaky the type errors will be baffling.
Build this: Be comfortable reading and writing modern JavaScript without a framework.
- 2
Week 1
Annotations and inference
Primitive types, arrays, objects, function signatures, union types, and — most importantly — where inference already does the job so you do not have to annotate.
Build this: Convert a small JavaScript project by renaming files and fixing errors one at a time.
- 3
Week 2
Narrowing and unions
`typeof`, `instanceof` and `in` guards, discriminated unions, optional properties, and `strictNullChecks` — the setting responsible for most of TypeScript's real-world value.
Build this: Model an async operation's states as a discriminated union and render each one.
- 4
Weeks 3–4
Generics and tsconfig
Type parameters, constraints, the built-in utility types (`Partial`, `Pick`, `Omit`, `Record`, `ReturnType`), and what each `tsconfig` flag actually changes. Turn on `strict` and leave it on.
Build this: Write a typed wrapper around `fetch` that infers the response type from a schema.
- 5
Ongoing
The advanced type system, used sparingly
Conditional and mapped types, template literal types, `satisfies`, declaration files, and runtime validation. Learn them so you can read library types — reach for them in your own code only when they earn their complexity.
Build this: Read the type definitions of a library you use daily and work out how they achieve their autocomplete.
Ecosystem and tooling
The tools you will end up installing whichever project you join.
| Tool | Category | What it does |
|---|---|---|
| tsc | Compiler | The official compiler and type checker; the reference for what TypeScript means |
| ts-node / tsx | Runtime | Run TypeScript directly without a separate build step during development |
| esbuild / SWC | Build | Extremely fast transpilers that strip types for builds, leaving type-checking to `tsc` |
| Zod / Valibot | Validation | Runtime schema validation that infers static types, closing the gap at I/O boundaries |
| typescript-eslint | Code quality | Lint rules that use type information — catches issues a syntax-only linter cannot |
| DefinitelyTyped | Types | Community type definitions (`@types/*`) for JavaScript libraries that ship none |
| tRPC | Framework | End-to-end typed client-server calls with no code generation or schema files |
| Vitest | Testing | Test runner with native TypeScript support and no configuration |
TypeScript libraries
28 catalogued, each with installation, worked examples and best practices.
Zod
Zod is a TypeScript-first schema declaration and validation library. One schema produces both the runtime check and the static type, so the two can never drift apart.
Developer UtilitiesValibot
Valibot is a schema validation library with an API similar to Zod's but built around tree-shakeable functions, producing dramatically smaller client bundles.
Developer UtilitiesTypeBox
TypeBox builds JSON Schema objects that also carry TypeScript types, so one definition serves runtime validation, static typing and OpenAPI documentation.
Developer UtilitiestRPC
tRPC gives you end-to-end type safety between a TypeScript server and client with no code generation, no schema files and no build step.
Web & HTTPPrisma
Prisma is a next-generation ORM with a declarative schema, generated type-safe client, and a migration system that produces reviewable SQL.
Databases & CachingDrizzle ORM
Drizzle is a TypeScript ORM whose query builder mirrors SQL closely, with no code generation step and a very small runtime suitable for serverless and edge environments.
Databases & Caching
Frequently asked
Does TypeScript make my code slower?
No. Types are erased during compilation, so the JavaScript that runs is the JavaScript you would have written. The cost is at build time, not runtime.
Is it worth it for a small project?
For a throwaway script, probably not. For anything you will return to in three months, or that anyone else will touch, the break-even point arrives much sooner than people expect — usually within a few hundred lines.
What does 'structural typing' mean in practice?
Compatibility is decided by shape, not by name. Any object with `id: number` and `name: string` satisfies an interface requiring those fields, whether or not it was declared to implement it. This is why typing existing JavaScript works so well, and why two unrelated types with identical fields are interchangeable.
Do I still need runtime validation?
Yes, for anything crossing a boundary: HTTP responses, form input, environment variables, files, message queues. The compiler can only reason about code it sees; everything else is a promise you should verify.
`interface` or `type`?
Either. `interface` supports declaration merging and gives marginally better errors for object shapes; `type` handles unions, intersections and mapped types. Pick one as the default for object shapes, use `type` where `interface` cannot, and stop debating it.
Will TypeScript become part of JavaScript?
There is a stage-1 proposal to let JavaScript engines ignore type annotations as comments, which would remove the build step for type stripping. It is genuinely early, and it would standardise the syntax rather than the checking.




