TypeScript is an open source programming language that extends JavaScript with optional static typing and compiles to plain JavaScript. Among programming languages, it is a typed superset of JavaScript: every valid JavaScript program is, with minor exceptions, also valid TypeScript, while TypeScript adds type annotations, interfaces, generics and other features that a compiler checks before the code runs.
TypeScript was released by Microsoft in 2012 and has become a common choice for large web front ends, Node.js services, desktop applications built with web technologies and tooling. Its value lies less in new runtime capabilities, since the output is ordinary JavaScript, than in finding errors early and making large codebases easier to understand and change.
How TypeScript works
TypeScript code is processed by the TypeScript compiler, tsc, or by a faster tool that only strips types. The compiler performs two separate jobs: type checking, which analyzes the code and reports errors, and emitting JavaScript, which removes type annotations and optionally converts newer syntax for older environments.
The key design principle is type erasure. Types exist only at compile time and leave no trace in the generated JavaScript. A function declared with a parameter of type number therefore performs no runtime check; if external data such as a network response has the wrong shape, the program fails only when the code uses it, unless the developer adds runtime validation explicitly.
TypeScript uses structural typing: two types are compatible if they have compatible shapes, regardless of their names. An object with the properties name and email satisfies any interface that requires those properties. This matches the way JavaScript code is written in practice, but it differs from the nominal typing of Java or C#, where types must be declared as related.
Core language features
TypeScript’s type system is unusually expressive, because it must describe the dynamic patterns common in existing JavaScript. The table lists the features developers use most often.
| Feature | Example | What it achieves |
|---|---|---|
| Type annotation | let count: number | Declares the expected type |
| Type inference | let count = 0 | Derives types without annotations |
| Interface and type alias | interface User { id: string } | Names the shape of objects |
| Union type | string | null | Allows one of several types |
| Generics | function first<T>(items: T[]) | Reusable code for many types |
| Narrowing | if (typeof x === "string") | Refines a type after a check |
| Utility types | Partial<User>, Readonly<User> | Transforms existing types |
Declaration files with the .d.ts extension describe the types of existing JavaScript libraries. They allow typed use of untyped code and are distributed either with the library itself or through community-maintained type packages.
TypeScript compared with neighboring languages
TypeScript is most often weighed against plain JavaScript, but comparisons with other statically typed languages clarify what its type system can and cannot promise.
| Language | Typing | Runtime type safety | Typical domain |
|---|---|---|---|
| TypeScript | Static, structural, gradual | No, types are erased | Web front ends, Node.js, tooling |
| JavaScript | Dynamic | Only runtime type errors | Browsers, Node.js, scripting |
| Java | Static, nominal | Yes, enforced by the virtual machine | Enterprise back ends, Android |
| Python with type hints | Gradual, checked by external tools | No, hints are not enforced | Scripting, data, back ends |
| Rust | Static, nominal, strict | Yes, with memory safety | Systems, performance-critical code |
The type system is intentionally unsound in some places, for example around array indexing and certain function parameter rules, to remain practical for real JavaScript. Developers should treat TypeScript as a strong safety net, not as a mathematical guarantee.
Configuration and adoption
A project’s behavior is controlled by the tsconfig.json file. Its options decide how strict the checks are, which JavaScript version is produced and how modules are resolved. The most important settings are summarized below.
| Option | Effect | Recommended setting |
|---|---|---|
| strict | Enables the full set of strict checks | On for new projects |
| noImplicitAny | Rejects values with an unknown type | On, part of strict |
| strictNullChecks | Treats null and undefined as separate types | On, part of strict |
| target | JavaScript version of the output | Match the oldest supported runtime |
| module and moduleResolution | Module system and lookup rules | Match the bundler or Node.js setup |
| allowJs | Mixes JavaScript files into the project | On during gradual migration |
Existing JavaScript projects are usually migrated incrementally: TypeScript is added with allowJs, files are renamed one by one, and strictness is raised over time. Some runtimes can now execute TypeScript files directly by stripping types, and a native port of the compiler announced in 2025 aims at much faster type checking on large codebases.
Limitations and common mistakes
Most problems in TypeScript projects come from trusting types more than the data, or from weakening the type system to silence errors.
- Overusing the any type turns off checking and spreads silently through the code; the unknown type is a safer alternative.
- Skipping runtime validation of external input, such as API responses or form data, because types disappear at runtime.
- Excessive type assertions, such as
value as User, which tell the compiler to trust the developer without proof. - Overly complex types that slow the compiler and are hard for colleagues to read.
- Version drift between the compiler, type packages and libraries, causing confusing errors after upgrades.
Understanding it simply
TypeScript is JavaScript with labels on the data. A programmer writes which kind of information each part of the program expects. Examples are a number, a piece of text or a customer record. A checking tool then reads the whole program before it runs.
If something does not fit, such as text where a number belongs, the tool points out the mistake right away. It is like a spelling checker for program logic. The finished program is still plain JavaScript, so it runs anywhere JavaScript runs.
Applied in practice
A team of eight developers maintains a booking web application with about 120,000 lines of JavaScript. Renaming a field in the customer object regularly broke screens nobody had tested, and these errors appeared only in production.
The team adds TypeScript with allowJs enabled and converts the shared data models first, then the most frequently changed screens. Over six months, they turn on strict mode module by module and add runtime validation for responses from the booking API. When a developer later renames the phoneNumber field, the compiler lists every one of the 37 places that must change before the code can be merged. The outcome is renaming and refactoring without fear, and a sharp drop in errors that first appear in front of customers.