cf.completefrontend
Open lab My progress
THE TYPESCRIPT FIELD GUIDE

keyof & typeof

Turn an object type into a union of its keys with keyof, and a value into a type with typeof, so key names and types come from the code you already have.

By the end, you can
  • 01
    List a type's keysUse keyof to get a union of an object type's property names, and know what it gives for index signatures and unions.
  • 02
    Copy a value's typeUse typeof in a type to reuse the type of a variable, a property, or a function, and tell it apart from JavaScript's typeof.
  • 03
    Type keys from your dataCombine keyof typeof and as const to write functions like get(obj, key) that accept only real keys.

Types made from names and from values

JavaScript can already tell you two things about an object while the page runs: what kind of value a property holds, and which property names the object has (the Checking types and Object.keys, values, entries lessons cover these):

Ask a cart about itself while the page runsPop out in the code editor (opens in a new tab)JavaScript
const cart = { items: ["Tea"], total: 3 };
console.log(typeof cart.total);
console.log(Object.keys(cart));

Line 1 makes a cart with a list of items and a total. Line 2 prints number, the kind of value in cart.total. Line 3 prints the cart's property names, [ 'items', 'total' ].

TypeScript lets you ask the same two questions while you write the code, and get types as answers:

  • typeof in a type gives the type of a value: "whatever type cart has".
  • keyof gives the property names of a type, as a union of texts, "this name or that one" (the Union types lesson covers |).
The cart's type, then its namesPop out in the code editor (opens in a new tab)TypeScript
const cart = { items: ["Tea"], total: 3 }; type Cart = typeof cart;//   ^?type CartKey = keyof Cart;//   ^?const key: CartKey = "total";console.log(key, cart[key]);

Line 1 is the same cart. Line 3 makes a type called Cart from the value: typeof cart. The query on line 4 shows what TypeScript worked out: items is a string[] and total is a number. You never wrote that type; it came from the value.

Line 5 makes CartKey from that type with keyof Cart, and the query on line 6 shows "items" | "total": one of the two names. Line 7 uses it for a variable, and line 8 reads cart[key] and prints total 3.

A shopping cart labelled cart holds the cards items and total: 3. An arrow marked typeof leads to a window called Cart that lists items: string[] and total: number. An arrow marked keyof leads to a card that says "items" | "total".itemstotal: 3carta valuetypeofCartitems: string[]total: numberits typekeyof"items" | "total"its names
typeof cart reads the type of the value on line 1. keyof Cart reads the names out of that type: "items" | "total".
Real-life analogyA filled-in form

A clinic hands you a filled-in form and asks for more like it. You photocopy it and blank out the answers: now you have the same boxes, ready for the next person. And if someone asks which boxes there are, you read the labels down the side: name, phone, date of birth.

In real life: A sign-up form someone filled in
In TypeScript: A value, like cart
In real life: A blank copy of that form, with the same boxes
In TypeScript: typeof cart, its type
In real life: The list of box labels on the form
In TypeScript: keyof Cart, its property names
In real life: Writing a label that isn't on the form
In TypeScript: A key that isn't in the union: an error
The main idea

typeof turns a value into its type, and keyof turns a type into a union of its property names. Both work only in types, before the code runs, so your types can come from code you already wrote instead of being typed out twice.

keyof: a type's property names

You used keyof in the Generic constraints lesson, inside K extends keyof T. It works on its own too. Put keyof before an object type, and you get the names of its properties. Here is a settings page's type:

The names of the settingsPop out in the code editor (opens in a new tab)TypeScript
type Settings = { theme: string; fontSize: number; showTips: boolean }; type SettingName = keyof Settings;//   ^?let field: SettingName = "fontSize";field = "theme";console.log(field);

Line 1 describes three settings. Line 3 makes SettingName with keyof Settings. The query on line 4 shows type SettingName = keyof Settings: when a type has a name, TypeScript keeps writing the short form. It means "theme" | "fontSize" | "showTips", one of those three names, as text. Line 5 and line 6 store two of them, and line 7 prints theme.

Any other text is an error, on purpose here:

A name the settings don't have (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
type Settings = { theme: string; fontSize: number; showTips: boolean }; let field: keyof Settings = "fontSize";field = "colour";

Line 4 stores "colour". TypeScript says Type '"colour"' is not assignable to type 'keyof Settings', which means "colour is not one of the names in Settings". The names are exact, so "fontsize" with a small s is an error too, and TypeScript adds Did you mean '"fontSize"'?

keyof works on any object type, not only ones with plain names. Press the buttons to change the type after keyof, and watch the answer on line 2:

Playground: keyof on different shapes
What keyof gives for one typePop out in the code editor (opens in a new tab)TypeScript
type Keys = keyof { id: number; name: string };//   ^?
After keyofPlain object
What TypeScript says
Keys"id" | "name"
TypeScriptNo errors
Try it yourself

Each property name becomes one text type, and they're joined with |: "id" | "name". keyof gives the names, never the values' types.

TypeScript checks the code for real each time. Each button changes only the type after keyof on line 1. Reset goes back to the plain object.

A few of these are worth remembering. A property marked ? or readonly still has a name. A type that accepts any text as a key (an index signature, which the Index signatures lesson covers) gives string | number. An empty type gives never, the type with no values. And a union of two object types gives only the names both have:

Two crates. The first holds the cards id and name, the second holds id and total. An arrow from the id cards leads to a card that says "id".idnameAoridtotalB"id"in bothkeyof (A | B)
A value of A | B is one of the two, so only a name in both crates is always there. keyof gives just "id".

The table below collects what keyof gives. Read each row as: the kind of type, an example, and the names you get.

What keyof gives for each kind of type
The typeExampleWhat keyof gives
An object type with named propertieskeyof { id: number; name: string }The names, joined: "id" | "name".
Optional or readonly propertieskeyof { id?: number; readonly name: string }The names still count: "id" | "name".
Any text as a keykeyof { [word: string]: number }string | number, because number keys become text.
Any number as a keykeyof { [index: number]: string }number.
No properties at allkeyof {}never: there are no names.
One of two object typeskeyof (A | B)Only the names both have.

typeof: the type of a value

Often the value comes first: a cart object, a coupon code, the shop's name. In a type, typeof followed by a variable's name gives that variable's type, the same type TypeScript shows when you hover over it:

Copy the types of three valuesPop out in the code editor (opens in a new tab)TypeScript
const cart = { items: ["Tea"], total: 3 };let coupon = "SAVE5";const shop = "Tea House"; type Cart = typeof cart;//   ^?type Coupon = typeof coupon;//   ^?type Shop = typeof shop;//   ^?

Lines 1 to 3 make three values. The query on line 6 shows the cart's object type. The query on line 8 shows string for the coupon: it's made with let, so it could hold any text later. The query on line 10 shows "Tea House": a const can never change, so its type is that exact text (the Literal types lesson covers this). typeof copies whatever type the value already has.

A function is a value too, so typeof gives you its type. That's handy when another function takes one like it:

A parameter typed like an existing functionPop out in the code editor (opens in a new tab)TypeScript
function formatPrice(price: number) {  return `$${price.toFixed(2)}`;} type Formatter = typeof formatPrice;//   ^?function showPrices(prices: number[], format: Formatter) {  console.log(prices.map(format).join(", "));}showPrices([3, 4.5], formatPrice);

Lines 1 to 3 make formatPrice, which turns a number into a price text. Line 5 copies its type, and the query on line 6 shows (price: number) => string: it takes a number and gives back a string. Line 7 says showPrices takes a list of prices and a function of that type. Line 10 passes formatPrice itself and prints $3.00, $4.50.

typeof only takes a name, or a name followed by dots. Type after typeof on line 6 and watch the answer on line 7:

Playground: take the type of a value
Copy the type of a valuePop out in the code editor (opens in a new tab)TypeScript
const cart = { items: ["Tea"], total: 3 };function formatPrice(price: number) {  return `$${price.toFixed(2)}`;} type Looked = typeof cart;//   ^?
After typeofcart
What TypeScript says
Looked{ items: string[]; total: number }
ErrorsNo errors
Try it yourself

typeof cart is the type TypeScript worked out for the cart object: a list of strings called items and a number called total.

The code is checked by the real TypeScript compiler each time you type. Only line 6 changes. Reset puts cart back.

Names and properties work. formatPrice(3) doesn't: typeof in a type never runs anything, so it can't call a function. A property that doesn't exist is an error, and so is a value written out, like "Tea".

JavaScript has its own typeof, which you met at the start. The word is the same; where you write it decides which one you get:

JavaScript's typeof and TypeScript's typeofPop out in the code editor (opens in a new tab)TypeScript
const total = 3; const kind = typeof total;//    ^?type Total = typeof total;//   ^?console.log(kind);

Line 3 is normal code: const kind = … runs with the page. So it's JavaScript's typeof, and the query on line 4 shows it gives one of eight texts, like "number" or "string". Line 5 is a type, after type Total =. So it's TypeScript's typeof, and the query on line 6 shows the type 3. Line 7 prints number.

Left: a console window shows the text number, from line 3, const kind = typeof total, which runs with the page. Right: an editor window shows type Total = 3, from line 5, which is removed before the code runs.const kind = typeof totalconsolenumberruns with the pagegives a texttype Total = typeof totaleditortype Total = 3removed before it runsgives a type
Line 3 is normal code, so JavaScript's typeof runs and gives the text "number". Line 5 is a type, so TypeScript's typeof gives the type 3, and it is gone before the code runs.

The rule: wherever a type goes, like after type X = or after the : that gives a variable its type, typeof is TypeScript's. In normal code, it's JavaScript's, and it runs. The JavaScript one is also how you check a value in an if (the Narrowing lesson covers that).

keyof typeof: the names of a value's properties

A list of page routes is a value. To get its names, you might reach for keyof straight away. That's an error, on purpose here:

keyof on a value (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
const routes = { home: "/", cart: "/cart", profile: "/me" }; type RouteName = keyof routes;

TypeScript says 'routes' refers to a value, but is being used as a type here. Did you mean 'typeof routes'?. keyof takes a type, and routes is a value that exists while the page runs. The message already says the fix:

The names of the routesPop out in the code editor (opens in a new tab)TypeScript
const routes = { home: "/", cart: "/cart", profile: "/me" }; type RouteName = keyof typeof routes;//   ^?function go(name: RouteName) {  console.log(`Going to ${routes[name]}`);}go("cart");

Read line 3 from the right. typeof routes is the object's type, and keyof takes its names. The query on line 4 shows "home" | "cart" | "profile". Lines 5 to 7 make go, which accepts only those names, and line 8 prints Going to /cart.

The routes are written once, in the object. Add a checkout route on line 1, and RouteName includes it with no other change.

typeof with as const

typeof copies the type TypeScript gave the value. For an object, each property gets a general type like string, because properties can be changed later. as const after the value says it will never change (the as const & satisfies lesson covers it). Press the buttons to add it and take it away:

Playground: typeof with and without as const
The type of a routes objectPop out in the code editor (opens in a new tab)TypeScript
const routes = { home: "/", cart: "/cart", profile: "/me" }; type Routes = typeof routes;//   ^?routes.home = "/start";console.log(routes.home);
Console output/start
What happens
homehome: string
TypeScriptNo errors
Try it yourself

Each property could change later, so TypeScript keeps the general type, string. Line 5 changes home, and TypeScript allows it.

TypeScript checks the code for real, and the output is what the code prints. Each button changes only the end of line 1. Reset takes as const away.

Without as const, the query on line 4 shows home: string, and line 5 may change it. With it, every property keeps its exact text and becomes readonly, so line 5 is marked. The keys are the same either way: keyof typeof routes is "home" | "cart" | "profile" in both.

Notice the output: both versions print /start. as const is a type, so it only makes TypeScript complain; it doesn't stop the change while the page runs. To stop that too, freeze the object (the Immutability lesson covers Object.freeze).

Two editor windows show typeof routes. Without as const: home: string and cart: string. With as const: readonly home: "/" and readonly cart: "/cart".typeof routeseditorhome: stringcart: stringwithout as consttypeof routeseditorreadonly home: "/"readonly cart: "/cart"with as const
Without as const, typeof gives the general type, because the object could change. With it, each property keeps its exact text and is readonly.

Lists work the same way:

The type of a list, with and without as constPop out in the code editor (opens in a new tab)TypeScript
const sizes = ["S", "M", "L"];const fixedSizes = ["S", "M", "L"] as const; type Sizes = typeof sizes;//   ^?type FixedSizes = typeof fixedSizes;//   ^?

Line 1 is a plain list, and the query on line 5 shows string[], a list of any texts. Line 2 adds as const, and the query on line 7 shows readonly ["S", "M", "L"]: exactly three items, in that order, that can't change. The next lesson turns that into the type "S" | "M" | "L".

A get function that only takes real keys

A settings page needs a function that reads one setting by its name. In JavaScript, you'd write settings[key] with any text. In TypeScript, that's an error, on purpose here:

A key typed as any text (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
const settings = { theme: "light", fontSize: 16, showTips: true }; function getSetting(key: string) {  return settings[key];}console.log(getSetting("fontSize"));

Line 3 says key is any text. Line 4 reads settings[key], and TypeScript says Element implicitly has an 'any' type because expression of type 'string' can't be used to index type …. In plain words: the settings only have three names, so "any text" might not be one of them, and TypeScript can't tell what you'd get back. The code still runs and prints 16, but a typo would quietly give undefined.

A key typed from the settings' own namesPop out in the code editor (opens in a new tab)TypeScript
const settings = { theme: "light", fontSize: 16, showTips: true }; function getSetting(key: keyof typeof settings) {  return settings[key];}const size = getSetting("fontSize");//    ^?console.log(size);

Line 3 types key with keyof typeof settings, so the error is gone and a typo gets marked. But the query on line 7 shows string | number | boolean: the function doesn't know which name you passed, so the result could be any setting's type.

The Generic constraints lesson fixed this with two type parameters: T for the object and K extends keyof T for the key, so K is the exact name you pass. The result, obj[key], then has the type of that one property, which TypeScript writes T[K] (the next lesson covers that form). Press the buttons to try different calls on line 6:

Playground: call get with different keys
A get function for any objectPop out in the code editor (opens in a new tab)TypeScript
function get<T, K extends keyof T>(obj: T, key: K) {  return obj[key];} const settings = { theme: "light", fontSize: 16, showTips: true };const value = get(settings, "fontSize");//    ^?console.log(value);
Console output16
What happens
valuenumber
TypeScriptNo errors
Try it yourself

K is "fontSize", so the result is the type of that one property: number. It prints 16.

TypeScript checks the code for real, and the output is what the code prints. Each button changes only line 6. Reset goes back to the first call.

Each call gets the exact type of its key, a wrong key is marked before the code runs, and the same get works for a user or any other object. keyof decides which keys are allowed; typeof is only needed when you start from a value rather than a type.

Feature flags from one object

Many apps turn features on and off with feature flags: a small object of names and true-or-false values. Write the object once, and let every type come from it:

Flag names read from the flags objectPop out in the code editor (opens in a new tab)TypeScript
const flags = { darkMode: true, newCheckout: false, chatHelp: true }; type FlagName = keyof typeof flags;//   ^?function isOn(name: FlagName) {  return flags[name];} console.log(isOn("newCheckout"));console.log(isOn("darkMode"));

Line 1 is the flags object. Line 3 reads its names, and the query on line 4 shows "darkMode" | "newCheckout" | "chatHelp". Lines 5 to 7 make isOn, which only takes those names. Line 9 prints false and line 10 prints true.

When the team adds a flag, they add one line to the object, and isOn accepts the new name. When they remove one, every call that still uses its name is marked. A typo like isOn("darkmode") is marked straight away instead of quietly reading undefined, which would turn the feature off.

Error or fine?
  • type Cart = { total: number }; type K = keyof Cart;
  • const cart = { total: 3 }; type K = keyof cart;
  • const cart = { total: 3 }; type K = keyof typeof cart;
  • type Cart = { total: number }; type T = typeof Cart;
  • const cart = { total: 3 }; type T = typeof cart.total;
  • function total() { return 3; } type R = typeof total();
  • const cart = { total: 3 }; const k: keyof typeof cart = "price";
  • let count = 3; type Count = typeof count;
Try it yourself
0 of 8 correct

Sort each line by what TypeScript says about it. Each line is checked on its own.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

JavaScript's Object.keys gives you an object's names while the page runs. You might expect its type to be the same union as keyof. It isn't, and this loop gets an error, on purpose here:

Looping over Object.keys (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
const flags = { darkMode: true, newCheckout: false }; const names = Object.keys(flags);//    ^?for (const name of names) {  console.log(name, flags[name]);}

The query on line 4 shows const names: string[]. So on line 6, name is any text, and flags[name] gets the same error as the loose getSetting. Why? TypeScript checks shapes, not exact objects: an object with extra properties still fits a type (the Structural typing lesson covers this). So Object.keys might return names the type doesn't list, and TypeScript only promises string[]. The code itself runs fine and prints both flags. Exercise 6 fixes it.

  • "keyof Cart gives the types of the values." No. It gives the property names: "items" | "total".
  • "keyof cart works on a value." No. keyof takes a type. Write keyof typeof cart.
  • "typeof in a type gives a text like "number"." No. That's JavaScript's typeof, in normal code. In a type, typeof gives the type itself.
  • "typeof formatPrice(3) gives the type of the result." No. typeof never calls anything; it only takes a name or a name with dots.
  • "typeof of a let gives its exact value." No. A let can change, so its type is general, like string. Only a const keeps the exact value.
  • "as const stops the object changing while the page runs." No. It's a type, so TypeScript only marks the change. Use Object.freeze to stop it.
  • "Object.keys(obj) is typed as the object's keys." No. It's string[], because an object may have more properties than its type lists.

The table below sets the JavaScript words beside the TypeScript ones. Read each row as: what you write, what it is, and what you get.

typeof and keys in JavaScript and in TypeScript
When you writeWhat it isWhat you get
typeof cart.total in normal codeJavaScript's typeof. It runs with the page.A text that names the kind of value: "number".
type T = typeof cart.totalTypeScript's typeof, in a type. It's removed before the code runs.The type of the value: number.
Object.keys(cart)A JavaScript function. It runs with the page.An array of the names the object has right now, typed string[].
keyof CartTypeScript's keyof, on a type. It's removed before the code runs.A union of the names: "items" | "total".

Practice exercises

Each exercise is TypeScript. Press Edit & run on the starter code: the errors and ^? answers update as you type. Try a guess first, then use the hints.

Exercise 1 · Warm-upFind the line TypeScript marks

Three lines store an order's property name. Which line will TypeScript mark?

Starter codePop out in the code editor (opens in a new tab)TypeScript
type Order = { id: number; status: string; total: number };

let key: keyof Order = "id";
key = "total";
key = "price";

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upPredict two typeof answers

    What type is Country? Guess first. Then press Edit & run and add a // ^? line under it to check.

    Starter codePop out in the code editor (opens in a new tab)TypeScript
    let city = "Pune";const country = "India"; type City = typeof city;type Country = typeof country;

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeType a translation key

      A shop shows its text in several languages, and t looks up a message by its key. Line 4 gets an error. Change the type of key on line 3 so only real message keys are allowed.

      Starter codePop out in the code editor (opens in a new tab)TypeScript
      const messages = { greeting: "Hello", cartTitle: "Your cart", checkout: "Pay now" }; function t(key: string) {  return messages[key];}console.log(t("cartTitle"));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeCopy a function's type

        Line 6 gets 'format' is of type 'unknown', because TypeScript doesn't know it's a function. Replace unknown on line 5 with the type of formatPrice, without writing it out by hand.

        Starter codePop out in the code editor (opens in a new tab)TypeScript
        function formatPrice(price: number) {  return `$${price.toFixed(2)}`;} function showTotal(total: number, format: unknown) {  console.log(format(total));}showTotal(7, formatPrice);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeLock the routes

          Line 5 changes a route, and TypeScript allows it. What do you add to line 1 so the routes keep their exact texts and line 5 is marked?

          Starter codePop out in the code editor (opens in a new tab)TypeScript
          const routes = { home: "/", cart: "/cart" }; type Routes = typeof routes;//   ^?routes.cart = "/basket";

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeLoop over an object's real keys

            This is the loop from the mix-ups section. Fix the error on line 6 by changing only line 3. What do you add after Object.keys(flags)?

            Starter codePop out in the code editor (opens in a new tab)TypeScript
            const flags = { darkMode: true, newCheckout: false }; const names = Object.keys(flags);//    ^?for (const name of names) {  console.log(name, flags[name]);}

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, ask: is this a type or a value, and is typeof here JavaScript's or TypeScript's? The quiz shows its code without the checker, so the error list doesn't give the answers away.

              The keyof and typeof quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What is keyof Cart, if type Cart = { items: string[]; total: number }?

                Choose an answer to see the explanation.

              2. Question 2 of 7What does line 4 of this price code print?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                const price = 4;
                type Price = typeof price;
                const kind = typeof price;
                console.log(kind);

                Choose an answer to see the explanation.

              3. Question 3 of 7What type is Size when size is made with let?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                let size = "M";
                type Size = typeof size;

                Choose an answer to see the explanation.

              4. Question 4 of 7Why does TypeScript mark line 3 of this routes code?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                const routes = { home: "/", cart: "/cart" };
                
                type RouteName = keyof routes;

                Choose an answer to see the explanation.

              5. Question 5 of 7What is keyof { [word: string]: number }?

                Choose an answer to see the explanation.

              6. Question 6 of 7In Theme here, what is the type of mode?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                const theme = { mode: "dark", size: 16 } as const;
                type Theme = typeof theme;

                Choose an answer to see the explanation.

              7. Question 7 of 7Why is Object.keys(flags) typed string[], not a list of the flag names?

                Choose an answer to see the explanation.

              Key takeaways

              • keyof Type is a union of the type's property names, like "items" | "total". Hovers often show it as keyof Type.
              • keyof on an index signature gives string | number or number, on {} gives never, and on a union gives only the shared names.
              • In a type, typeof value is the value's type. It takes a name or a name with dots, and never calls anything.
              • In normal code, typeof is JavaScript's and gives a text like "number" while the page runs.
              • keyof typeof obj gives the names of a value's properties, so they're written only once.
              • With as const, typeof keeps exact values and adds readonly. It changes the type, not the running object.
              • Object.keys is typed string[], because objects can have extra properties.

              Remember the one-liner.
              typeof turns a value into its type, and keyof turns a type into its names.

              Coming next: Indexed access types. Once you have a key, they look up the type of that one property, like Cart["total"].

              CompleteFrontend Clear concepts. Working examples.