Skip to main content

· 20 min read

"TypeScript", or "TS" for short, receives a lot of attention and discussion online. You might hear phrases like "TypeScript catches my bugs" or "TypeScript lets me write my types". If you've never worked with TypeScript before, it can seem like the word "TypeScript" refers to a whole collection of features or tools.

That's because "TypeScript" can refer to several different tools: four, in fact. We'll explore what those four things are in this blog post, as well as why you'd want to use them.

1. Language

"TypeScript" most commonly refers to the TypeScript programming language. By "programming language", we mean a set of syntax that describes how to write code.

The TypeScript language includes all of JavaScript's syntax, plus new syntax to describe "types": how values in code are expected to behave. That's why developers often call TypeScript a "superset of JavaScript" 1, or "JavaScript with syntax for types".

The following block of code is an example of TypeScript syntax:

ts
function double(value: number) {
return value * 2;
}
ts
function double(value: number) {
return value * 2;
}

The : number characters in that code block are an example of TypeScript-specific types syntax. They're a "type annotation" telling the reader that the value parameter is meant to be a number. Everything else in the code block is valid JavaScript.

2. Type Checker

A "type checker" is a program that:

  1. Reads in source code syntax
  2. Checks if anything seems to be used in ways that won't work as expected

The TypeScript type checker is a program that checks JavaScript and/or TypeScript source code. It lets you know if the way any value is used seems to not match with the way it was declared or later modified. A mismatch between the declared intent of a value with how it's used is typically called a "type error".

For example, providing an argument of one type to a function whose parameter declares a different type is a type error:

ts
function double(value: number) {
return value * 2;
}
 
double("oops!");
Argument of type 'string' is not assignable to parameter of type 'number'.2345Argument of type 'string' is not assignable to parameter of type 'number'.
ts
function double(value: number) {
return value * 2;
}
 
double("oops!");
Argument of type 'string' is not assignable to parameter of type 'number'.2345Argument of type 'string' is not assignable to parameter of type 'number'.

The logic a type checker uses to produce type errors is called a "type system". TypeScript's type system describes what its type checker should report based on different combinations of types and values in code.

Developers often run TypeScript's type checker via 3. Compiler and 4. Language Services.

tip

Type checkers are a form of "static analysis": meaning they analyze your source code without running it. Tools such as test runners that run code and analyze the result are referred to as "dynamic analysis".

3. Compiler

A compiler is a program that takes code in a high-level language and outputs a transformed version of that code in a lower-level language. Compilers for typed languages such as TypeScript generally run type checking as a part of compiling.

TypeScript provides a compiler with its command-line, tsc. tsc is able to read in TypeScript source files, type check those files, output the equivalent output JavaScript files, and log any type errors it found during type checking.

Running tsc on a file with a type error might look like:

plaintext
$ tsc index.ts
index.ts:5:8 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
5 double("oops!");
~~~~~~~
Found 1 error in index.ts:6
plaintext
$ tsc index.ts
index.ts:5:8 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
5 double("oops!");
~~~~~~~
Found 1 error in index.ts:6

TypeScript's tsc command-line is highly configurable. You can read more about it on TypeScript Handbook > tsc CLI Options

4. Language Services

Language services are tools that allow editors such as VS Code to interact with the language's programs. TypeScript's language services allow editors to:

  • Compile TypeScript source files in real-time as you edit them
  • Indicate types and type errors as you edit code or select values with your cursor
  • Ask TypeScript for automated actions such as finding all references or renames

This screenshot shows VS Code visualizing a type error with a red squiggly and a popup containing the error's message:

The aforementioned double("oops!") snippet in VS Code, with a red squiggly under the string, and the assignability type error in a hover message

Language services in editors are a powerful advantage for developers working in typed languages such as TypeScript.

Why Use TypeScript?

TypeScript means four separate things, three of which are programs. Many web developers swear by those four TypeScripts and use each to write much of rhtier web applications. Why are so many developers so passionate about TypeScript?

Bug Checking

TypeScript's type checker is a powerful program to check whether the intent of code matches up with usage. Type checking can detect many mistakes at development-time -- before the code is run in tests or by users. A research study in 2017 found TypeScript able to catch upwards of 15% of observed bugs 2.

No single strategy or tool can eliminate all bugs from an application. But eliminating 15% or more of bugs at development-time is quite appealing.

Development Assistance

TypeScript's language services provide a suite of features that can make coding faster and more reliable. These features are often referred to as "IntelliSense", a term Microsoft uses to refer to features in IDEs.

Commonly used development assistance provided by TypeScript's language services include:

  • Go-to-definition: going from a place a construct is used to where it was defined
  • Find-all-references: given a the definition, listing all the places it's used
  • Rename: updating a name in every place in code that refers to it

Many developers prefer using TypeScript's language service features whe possible because it saves time and reduces risk of human error. Renaming a variable, for example, can take a long time if there are many places that use the variable. Common names like "value" can also be difficult to manually rename if other variables share that name.

You can read more about editor features in VS Code's IntelliSense guide.

Standardized Code Documentation

Although JavaScript allows writing comments to describe code, there is no formal specification for how to use those comments. The JSDoc community standard for writing comments is only a loose standard and does not contain a formal type checker.

TypeScript types, on the other hand, are a formalized way to describe the intent behind code values. Types are verified by type checking to have valid syntax and match up with values. Many TypeScript developers view fully describing a project's types as one of the first necessary step in its documentation.

Next Steps

The official TypeScript Website is an excellent introduction to the TypeScript language and tooling. Its TypeScript Handbook includes Getting Started guides for developers with different areas and levels of experience.

Learning TypeScript (O'Reilly) -the book associated with this blog- is a community resource for TypeScript. It covers the foundations of TypeScript, how to work with its type system, and the aspects of its type checker and type system used by most projects.


This article is a text version of TypeScript in 2 minutes ft Josh Goldberg.

Got your own TypeScript questions? Ask @learningtypescript.com on Bluesky and the answer might become an article too!


  1. TypeScript is not an exact superset of JavaScript. There is a small set of syntax edge cases where the two languages differ. It's exceedingly unlikely to hit those edge cases during development.
  2. Gao, Z., Bird, C., & Barr, E. T. (n.d.). To type or not to type: Quantifying detectable bugs in JavaScript. Retrieved from https://www.microsoft.com/en-us/research/wp-content/uploads/2017/09/gao2017javascript.pdf.

· 20 min read

Once in a while in TypeScript code, you may come across a comment that looks something like this:

ts
// @ts-expect-error
let value: number = "oops!";
ts
// @ts-expect-error
let value: number = "oops!";

You may also know that // @ts-expect-error suppresses type checking for a single line. Wanting to ignore type errors might seem counterintuitive at first. If a project already decided to use TypeScript, why disable type checking?

Rare cases do exist when TypeScript's type checking needs to be suppressed in a small area of code. TypeScript includes several "comment directives" that can change type checking behavior for a file or on a specific line. A comment directive is a comment that directs (changes) how a tool operates within a file.

This article will explain the kinds of comment directives supported by TypeScript: why they exist, what they do, and how to use them.

· 20 min read

TypeScript's type system allows any two types that seem to have the same structure to be assignable to each other. But what if we want to restrict a type to only allow certain values, even if other values happen to have the same structure? Say, marking a difference between sanitized and un-sanitized strings, or positive integers from all numbers?

This need is solvable with a pattern called "branded types", sometimes also called "opaque types". Let's dive into why one might want to use branded types, how to declare and use them, and some alternatives to the pattern.

· 20 min read

One long-requested feature for TypeScript is the ability for its types to describe what exceptions a function might throw. "Throw types", as the feature is often called, are used in some programming languages to help ensure developers call functions safely.

The popular strongly typed language Java, for example, implements throw types with a throws keyword. Without reading any other code, a developer would infer from the following first line of a positive function that the function is able to throw a ValueException:

java
public static void positive(int value) throws ValueException { /* ...*/ }
java
public static void positive(int value) throws ValueException { /* ...*/ }

Throw types are also useful for developer tooling. They can tell compilers when a function call might throw an exception without safe try/catch handling.

That all seems useful, so why doesn't TypeScript include throw types?

In short, doing so wouldn't be feasible for TypeScript -- and some would argue isn't practical in most programming languages. This blog post will dig into the benefits, drawbacks, and general blockers to including throw types in TypeScript. Let's dig in!

· 20 min read

TypeScript's provides several ways to describe the type of a function that can be called in multiple different ways. But the two most common strategies -function overloads and generic functions- don't help much with how the function's internal implementation understands the types of its parameters.

This article will walk through three techniques for describing a parameter type that changes based on a previous parameter. The first two allow using standard JavaScript syntax but aren't quite precise in describing the types inside the function. The third one requires using a ... array spread and [...] tuple type in a funky new way that gets the right types internally.

· 20 min read

Most projects in the JavaScript/TypeScript ecosystem release new versions with numbers respecting semantic versioning, or semver for short. Semver is a specification that describes how to predictably increase a package's version numbers upon each new release. TypeScript is notable for not following a strict interpretation of semver for its releases. This article will dig into:

  1. What semver is and why it's useful for many packages
  2. Why following a strict interpretation of semver would be impractical for TypeScript
  3. How TypeScript's releases are versioned to an interpretation of semver that makes sense for it

While TypeScript's diverging from a common community specification can be irksome for developers, there are real reasons why it chose to diverge.

The reasoning can be summarized as:

  • Nuances of TypeScript's type checking change in virtually every release
  • It would be impractical to increase TypeScript's major version for every type checking change
  • If we consider those type checking nuances as details rather than the public API, TypeScript actually has quite good adherance to semantic versioning

This article will also more deeply explain each of those points.

· 20 min read

TypeScript is a highly configurable language. It comes with over a hundred compiler options that can be provided via the command-line to tsc and/or in a "TSConfig" configuration file (by default, tsconfig.json).

tip

TypeScript's compiler options are documented at aka.ms/tsconfig.

compilerOptions.target in particular can be an important configuration option for your project. It specifies which ECMAScript version your project's output JavaScript code must support.

You can specify target in your TSConfig as the string name of an ECMAScript version, such as "es5"or"es2021":

jsonc
// tsconfig.json
{
"compilerOptions": {
"target": "es2021"
}
}
jsonc
// tsconfig.json
{
"compilerOptions": {
"target": "es2021"
}
}

This article explores what target influences and why that's useful. Let's dig in!

· 15 min read

TypeScript 4.9 introduces a new operator, satisfies, that allows opting into a different kind of type inference from the type system's default. satisfies brings the best of type annotations and default type inference together in a useful manner. Let's explore the new satisfies operator and why it's useful!

· 20 min read

TypeScript's type narrowing is a powerful feature of TypeScript's type system that lets it infer more specific types for values in areas of code. For example, TypeScript would understand that inside the following if statement, the fruit variable has to be the literal value "apple":

ts
const fruit = Math.random() > 0.5 ? "apple" : undefined;
 
fruit;
const fruit: "apple" | undefined
 
if (fruit) {
fruit;
const fruit: "apple"
}
ts
const fruit = Math.random() > 0.5 ? "apple" : undefined;
 
fruit;
const fruit: "apple" | undefined
 
if (fruit) {
fruit;
const fruit: "apple"
}

But, TypeScript's type system isn't perfect. There are some cases where TypeScript can't narrow types exactly the way you might want.

Take a look at this code snippet, where counts.apple is inferred to be type number:

ts
const counts = {
apple: 1,
};
 
counts.apple;
(property) apple: number
ts
const counts = {
apple: 1,
};
 
counts.apple;
(property) apple: number

While counts is type { apple: number }, shouldn't TypeScript know that the immediately available value of counts.apple is specifically the literal type 1 and not the general primitive type number? Can't TypeScript tell we haven't changed the value yet?

It could, but it won't. And for very good reason.

· 20 min read

TypeScript's type system is Turing Complete: meaning it has conditional branching (conditional types) and works with an arbitrary huge amount of memory. As a result, you can use the type system as its own programming language complete with variables, functions, and recursion. Developers have pushed the bounds of type operations possible in the type system to write some pretty incredible things!

This blog post is a starting list of nifty things TypeScript developers have pushed the type system to be able to do. They range from binary arithmetic and rudimentary virtual machines to maze solvers and full programming languages.

✋ This Is Not Normal

Most applications try to get away with as few extreme type operations as possible. Complex logic in the type system gets unreadable and hard to debug pretty quickly. The Learning TypeScript book advises:

If you do find a need to use type operations, please—for the sake of any developer who has to read your code, including a future you—try to keep them to a minimum if possible. Use readable names that help readers understand the code as they read it. Leave descriptive comments for anything you think future readers might struggle with.

Please don't look at the following projects list and think you need to understand them to use TypeScript. These projects are ridiculous. They're the equivalent of code golf: a fun activity for a select few, but not useful for most day-to-day work.