Skip to content
Theory · Type operators and utility types · lesson 3 of 8

typeof in types

You don't have to type the fields into the form generator by hand. It is enough to feed it a form that is already filled in. The generator reads which fields the form has and what is written in them, and makes a blank form from it. A new field on the sample shows up on the blank too.

Programs have samples like that all the time. An object with settings is already written, and a function that takes settings needs its type. You can describe the type by hand: type Settings = { theme: string; fontSize: number }. But then every field is written twice, and you must remember to add a new field to both the object and the type.

typeof takes the type of a value

The typeof operator in a type position takes a value and returns its type, the one TypeScript has for that value:

TypeScript
const settings = { theme: "dark", fontSize: 14 };

type Settings = typeof settings;

function applySettings(s: Settings) {
    console.log(s.theme, s.fontSize);
}

applySettings({ theme: "light", fontSize: 16 }); // light 16
console.log(typeof settings); // object

Let's walk through the example line by line:

  1. settings is an ordinary object. TypeScript infers the type { theme: string; fontSize: number; } for it.
  2. type Settings = typeof settings gives that type a name. No field had to be written again.
  3. applySettings takes an object of the same shape and prints light 16.
  4. The last line prints object. Here typeof is a different operator, and the next section is about it.

typeof takes the inferred type, not the values themselves. theme has the type string, not "dark", because TypeScript widens object properties the same way it widens let variables.

Here the form generator differs from typeof. The generator reads a real paper, while typeof looks only at the type TypeScript inferred at compile time. It does not know what the object will hold while the program runs.

Two different typeofs

typeof appears twice in the example, and these are two different operators. You can tell which one you are looking at by where it stands.

typeof in code

This is the JavaScript operator. It works at run time and returns a string that names the kind of value: "object", "number", "string". You can print the string, compare it or store it in a variable. This is the operator that does the narrowing in typeof v === "number".

typeof in a type

This is the TypeScript operator. It stands where a type is expected: after type Settings = or after the colon of an annotation. It works at compile time, returns a type and vanishes from the JavaScript together with the other types.

Both operators take a value on their right: the name of a variable, a function or an object.

A list of values as a union

typeof in types is especially useful together with as const. Remember the type Size: we listed its choices by hand, type Size = "S" | "M" | "L". If the program also has an array of these sizes, every size is written twice. Now you can get the type from the array itself:

TypeScript
const sizes = ["S", "M", "L"] as const;

type Size = (typeof sizes)[number];

function order(size: Size) {
    console.log(size);
}

order("M"); // M
order(sizes[2]); // L

Let's see how the type Size came about:

  1. as const keeps the literals, and typeof sizes has the type readonly ["S", "M", "L"].
  2. [number] is the same indexed access as User["age"], except that the key here is any number, that is, the index of any element.
  3. Any element of the array is "S", "M" or "L", so Size is the union "L" | "M" | "S".
  4. The calls order("M") and order(sizes[2]) compile and print M and L.

The parentheses around typeof sizes are there for the reader: first you take the type of the array, then the type of its element. tsc does not let through a size that is not on the list—the call order("XXL") is an error:

TEXT
error TS2345: Argument of type '"XXL"' is not assignable to parameter of type '"L" | "M" | "S"'.

The list of sizes is written once. Add "XL" to the array, and the type Size accepts it right away.

Without as const the trick does not work. The array ["S", "M", "L"] has the type string[], so (typeof sizes)[number] gives a plain string. Any string fits such a parameter, and tsc says nothing.

Interviewers often ask how to get a union from an array of values. The answer is as const on the array and (typeof sizes)[number].

Common mistake

typeof is written before a type:

TypeScript
interface User {
    name: string;
    age: number;
}

type Person = typeof User; // ✗
TEXT
error TS2693: 'User' only refers to a type, but is being used as a value here.

tsc says that the name User refers only to a type, while here it stands where a value goes. typeof expects a value on its right, and an interface has no value: it exists only for the compiler. There is no need to take the type of a type—write type Person = User or use User directly.

Key points

  • typeof settings in a type position gives the type of the value settings, as TypeScript inferred it.
  • typeof in code is the JavaScript operator: at run time it returns a string such as "object". typeof in a type vanishes at compile time.
  • (typeof sizes)[number] together with as const turns a list of values into the union "L" | "M" | "S".
  • typeof always takes a value on its right: typeof User for an interface is the error TS2693.