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:
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); // objectLet's walk through the example line by line:
settingsis an ordinary object. TypeScript infers the type{ theme: string; fontSize: number; }for it.type Settings = typeof settingsgives that type a name. No field had to be written again.applySettingstakes an object of the same shape and printslight 16.- The last line prints
object. Heretypeofis 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:
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]); // LLet's see how the type Size came about:
as constkeeps the literals, andtypeof sizeshas the typereadonly ["S", "M", "L"].[number]is the same indexed access asUser["age"], except that the key here is any number, that is, the index of any element.- Any element of the array is
"S","M"or"L", soSizeis the union"L" | "M" | "S". - The calls
order("M")andorder(sizes[2])compile and printMandL.
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:
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:
interface User {
name: string;
age: number;
}
type Person = typeof User; // ✗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 settingsin a type position gives the type of the valuesettings, as TypeScript inferred it.typeofin code is the JavaScript operator: at run time it returns a string such as"object".typeofin a type vanishes at compile time.(typeof sizes)[number]together withas constturns a list of values into the union"L" | "M" | "S".typeofalways takes a value on its right:typeof Userfor an interface is the errorTS2693.