cf.completefrontend
Open lab My progress
THE TYPESCRIPT FIELD GUIDE

ReturnType, Awaited & more

Use TypeScript's Exclude, Extract, NonNullable, ReturnType, Parameters, and Awaited to build new types from unions, functions, and promises you already have.

By the end, you can
  • 01
    Filter a unionTake members out of a union with Exclude, keep only some with Extract, and drop null and undefined with NonNullable.
  • 02
    Read a function's typesGet what a function returns with ReturnType and what it takes with Parameters, using typeof to turn the function into a type.
  • 03
    Name the data a promise givesUnwrap a promise with Awaited, and combine it as Awaited<ReturnType<typeof f>> for an async function.

New types from the types you have

A shop tracks every order with a status: pending, paid, shipped, or cancelled. A reminder email should only go out for orders that are still open, pending or paid. You could write a second list of statuses by hand, but then two lists must be kept the same forever. TypeScript can make the new type from the one you already have:

Open statuses made from all statuses (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
type OrderStatus = "pending" | "paid" | "shipped" | "cancelled";type OpenStatus = Exclude<OrderStatus, "shipped" | "cancelled">;//   ^? function remindShopper(status: OpenStatus) {  console.log(`Your order is ${status}`);}remindShopper("paid");remindShopper("cancelled");

Line 1 lists the four statuses as a union: a type that allows one of several values, written with | between them (the Union types lesson covers them). Each value in the list is called a member. Line 2 makes OpenStatus with Exclude, which takes two types in angle brackets: the union to start from, and the members to take out. The query on line 3 shows the result: type OpenStatus = "pending" | "paid".

Lines 5 to 7 are a function that only takes an open status. Line 8 passes "paid", which is fine. Line 9 passes "cancelled", and TypeScript marks it, on purpose:

  • Argument of type '"cancelled"' is not assignable to parameter of type 'OpenStatus' means ""cancelled" isn't one of the values OpenStatus allows". An argument is a value you pass in a call.

When the code runs, it prints Your order is paid and then Your order is cancelled: types are removed before the code runs, so the error is a warning for you, not a stop.

Four cards for OrderStatus: "pending", "paid", "shipped", and "cancelled". An arrow leads into Exclude, which takes out "shipped" and "cancelled". An arrow leads out to OpenStatus, with two cards left: "pending" and "paid".OrderStatus"pending""paid""shipped""cancelled"Excludetakes out"shipped" | "cancelled"OpenStatus"pending""paid"what is left
Exclude goes through the four members of OrderStatus and takes out the ones that fit "shipped" | "cancelled". Two are left, and they make OpenStatus.

Exclude is one of TypeScript's utility types: type helpers that come with TypeScript, ready to use in any file. The Partial, Pick, Omit & friends lesson covered the ones for object types. This lesson covers six more, for unions, functions, and promises:

  • Exclude and Extract take members out of a union, or keep only some.
  • NonNullable takes out null and undefined.
  • ReturnType and Parameters read what a function gives back and what it takes.
  • Awaited gives the value inside a promise.
Real-life analogyA sieve in the kitchen

You pour a pot of soup through a sieve. Some days you want the broth, so you keep what runs through. Other days you want the vegetables, so you keep what the sieve catches. Same pot, same sieve, two different results, and the recipe stays as it was.

In real life: A pot of soup with vegetables in it
In TypeScript: A union, like OrderStatus
In real life: A sieve with holes of one size
In TypeScript: The second type, like "shipped" | "cancelled"
In real life: Keep the broth that runs through
In TypeScript: Exclude keeps the members that don't fit the second type
In real life: Keep the vegetables the sieve catches
In TypeScript: Extract keeps the members that do fit
In real life: The recipe card for the soup is unchanged
In TypeScript: OrderStatus stays as it was; you get a new type
The main idea

A utility type is a built-in type helper. You give it a type in angle brackets, like Exclude<OrderStatus, "cancelled">, and it gives back a new type. The type you started from doesn't change, and when it does change later, the new type follows.

Filtering a union with Exclude and Extract

Exclude<Union, Remove> goes through Union one member at a time. For each member, it asks: does this fit Remove? "Fits" means the value would be allowed where Remove is the type; TypeScript's word for it is assignable. Every member that fits is taken out. Type your own second type on line 3, and watch the answer after ^? on line 4:

Playground: what to take out
Take members out of OrderStatusPop out in the code editor (opens in a new tab)TypeScript
type OrderStatus = "pending" | "paid" | "shipped" | "cancelled"; type Result = Exclude<OrderStatus, "cancelled">;//   ^?const next: Result = "paid";console.log(next);
Taken out"cancelled"
What happens
Result is"pending" | "paid" | "shipped"
TypeScriptNo errors
Try it yourself

Exclude takes "cancelled" out of OrderStatus, and the answer after ^? lists the three that are left. "paid" is one of them, so line 5 is fine.

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

With "shipped" | "cancelled", two statuses are left, as in the overview. With "paid", line 5 gets an error, because "paid" was taken out. With "canceled", spelled with one l, nothing is taken out and TypeScript says nothing: Exclude doesn't check that the second type is part of the union. With string, every status fits and is taken out, so nothing is left. TypeScript calls that never, the type with no values at all. cancelled without quotes gets Cannot find name 'cancelled', because a name without quotes means a type with that name.

Extract<Union, Keep> is the opposite. It keeps only the members that fit Keep. It's most useful with a union of object types, like the events a shopping cart sends when something changes:

Pick the add event out of all cart eventsPop out in the code editor (opens in a new tab)TypeScript
type CartEvent =  | { type: "add"; name: string; price: number }  | { type: "remove"; name: string }  | { type: "clear" }; type AddEvent = Extract<CartEvent, { type: "add" }>;//   ^?type OtherEvent = Exclude<CartEvent, { type: "add" }>;//   ^? function showAdded(event: AddEvent) {  console.log(`Added ${event.name} for $${event.price}`);}showAdded({ type: "add", name: "Tea", price: 3 });

Lines 1 to 4 describe three kinds of event. Each has a type property that says which kind it is (the Discriminated unions lesson covers this pattern). Line 6 keeps the members that fit { type: "add" }. An object type fits when it has at least those properties, so only the add event does. The query on line 7 shows it, with its name and price too.

Line 8 uses Exclude with the same check, and the query on line 9 shows the other two events. Lines 11 to 13 are a function that only takes add events, so it can read event.price: every AddEvent has one. Line 14 prints Added Tea for $3.

Three cards for CartEvent: the "add" event, the "remove" event, and the "clear" event. Each is checked against { type: "add" }. The add event fits, and an arrow takes it to Extract. The remove and clear events do not fit, and arrows take them to Exclude.CartEvent"add" event"remove" event"clear" event{ type: "add" }fitsdoesn't fitExtract"add" eventExclude"remove" event"clear" event
The same check, { type: "add" }, splits CartEvent in two. Extract keeps the one event that fits it. Exclude keeps the other two.

The table below puts the two helpers side by side. Read each row as: what you write, which members it keeps, and the result.

Exclude and Extract, side by side
When you writeWhat it keepsThe result
Exclude<OrderStatus, "shipped" | "cancelled">The members that don't fit the second type."pending" | "paid"
Extract<OrderStatus, "shipped" | "cancelled" | "refunded">The members that fit the second type. "refunded" isn't a member, so it doesn't appear."shipped" | "cancelled"
Extract<CartEvent, { type: "add" }>The object types that have at least type: "add".{ type: "add"; name: string; price: number; }

Removing null and undefined with NonNullable

A value that might be missing often has a type like string | null. null means "nothing here, on purpose", and undefined means "not set" (see null and undefined). NonNullable<T> takes both out of T. Here a profile form has an optional nickname:

A badge that needs a real nickname (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
type ProfileForm = {  name: string;  nickname: string | null;}; type Nickname = NonNullable<ProfileForm["nickname"]>;//   ^? function showBadge(nickname: Nickname) {  console.log(`Hi, ${nickname.toUpperCase()}!`);} const form: ProfileForm = { name: "Asha", nickname: "Ash" };if (form.nickname) showBadge(form.nickname);showBadge(form.nickname);

Lines 1 to 4 describe the form. The nickname can be left empty, so it's string | null. On line 6, ProfileForm["nickname"] means "the type of the nickname property in ProfileForm", which is string | null. NonNullable takes the null out, and the query on line 7 shows type Nickname = string.

Lines 9 to 11 show a badge. Line 10 can call toUpperCase, because a Nickname is always text. Line 13 fills in the form. Line 14 checks the nickname first: inside the if, it can't be null (the Narrowing lesson explains this). Line 15 doesn't check, and TypeScript marks it on purpose:

  • Argument of type 'string | null' is not assignable to parameter of type 'string' means "this nickname might be null, and the badge needs text".

This form has a nickname, so the code prints Hi, ASH! twice. With nickname: null, line 15 would throw a TypeError when it runs, because null has no toUpperCase.

NonNullable is most useful with a type parameter, where you don't know the exact type ahead of time (see Generic functions). This helper makes sure a value is there:

A value that must be therePop out in the code editor (opens in a new tab)TypeScript
function requireValue<T>(value: T, label: string): NonNullable<T> {  if (value === null || value === undefined) {    throw new Error(`${label} is missing`);  }  return value;} const params = new URLSearchParams("city=Pune");const savedCity = params.get("city");//    ^?const city = requireValue(savedCity, "city");//    ^?console.log(city.toUpperCase());

Line 1 says requireValue takes a value of any type T and a label, and gives back NonNullable<T>. Lines 2 to 4 throw an error when the value is null or undefined, so line 5 only runs with a real value, and TypeScript knows it. That's why return value fits NonNullable<T>.

Line 8 reads the part of a web address after the ?, here city=Pune, with URLSearchParams. Line 9 asks for the city. The query on line 10 shows string | null, because an address might not have one. Line 11 passes it through requireValue, and the query on line 12 shows const city: string. Line 13 prints PUNE.

How TypeScript writes these helpers

Exclude, Extract, and NonNullable are one line each in TypeScript's own library:

  • type Exclude<T, U> = T extends U ? never : T; For each member of T: if it fits U, it becomes never and drops out. If not, it stays.
  • type Extract<T, U> = T extends U ? T : never; The same question, with the answers swapped.
  • type NonNullable<T> = T & {}; Here {} means any value except null and undefined, so only the other parts of T are kept.

The Conditional types lesson explains the ? : form. ReturnType, Parameters, and Awaited are written with infer, like the ReturnOf, ParamsOf, and UnwrapAll you wrote in The infer keyword. The next lesson writes helpers like these yourself.

A function's types with ReturnType and Parameters

The next two helpers read a function's type. They are the built-in versions of the ReturnOf and ParamsOf you wrote in The infer keyword. A helper takes a type, and a function is a value. The word typeof, written where a type goes, turns a value into its type (keyof & typeof covers it). typeof createCartItem means "the type of the function createCartItem": what it takes and what it gives back.

Types read from a functionPop out in the code editor (opens in a new tab)TypeScript
function createCartItem(name: string, price: number, quantity = 1) {  return { name, price, quantity };} type CartItem = ReturnType<typeof createCartItem>;//   ^?type ItemArgs = Parameters<typeof createCartItem>;//   ^? const args: ItemArgs = ["Cake", 4];const cake: CartItem = createCartItem(...args);console.log(cake);

Line 1 makes a cart item from a name, a price, and a quantity. The quantity is 1 unless you pass one: a default value (see Parameters). Line 2 returns the three as an object. TypeScript works out what the function returns, so nobody wrote that type down.

Line 5 names it with ReturnType, and the query on line 6 shows { name: string; price: number; quantity: number; }. Line 7 uses Parameters. The query on line 8 shows a tuple, a list with a fixed length and a type for each place: [name: string, price: number, quantity?: number | undefined]. The ? means the quantity may be left out.

Line 10 makes a list of arguments that fits ItemArgs, and line 11 spreads them into the call with ... (see Spread and rest). Line 12 prints { name: 'Cake', price: 4, quantity: 1 }.

In the middle, the function createCartItem. On the left, Parameters: name string, price number, and an optional quantity number, with an arrow into the function. On the right, ReturnType: name string, price number, quantity number, with an arrow out of the function.Parametersname: stringprice: numberquantity?: numberwhat goes increateCartItemtypeof createCartItemReturnTypename: stringprice: numberquantity: numberwhat comes out
Parameters reads what goes in: a name, a price, and an optional quantity. ReturnType reads what comes out: an object with all three. Both start from typeof createCartItem.

Press the buttons to write line 5 five ways, and watch the answer after ^? and the list under the code:

Playground: read a function's types
A helper reading createCartItemPop out in the code editor (opens in a new tab)TypeScript
function createCartItem(name: string, price: number, quantity = 1) {  return { name, price, quantity };} type Result = ReturnType<typeof createCartItem>;//   ^?
Result is{ name: string; price: number; quantity: number; }
What happens
TypeScriptNo errors
Try it yourself

ReturnType gives the type of what createCartItem returns: an object with a name, a price, and a quantity. You never wrote that type; TypeScript read it from line 2.

TypeScript checks the code for real, and the answer after ^? on line 6 is its own. Each button changes only line 5. Reset goes back to ReturnType.

ReturnType and Parameters give the two types from the picture. Parameters, item 0 adds [0], which reads the first place of the tuple: string, the name. Without typeof gets 'createCartItem' refers to a value, but is being used as a type here, and the message ends with the fix, Did you mean 'typeof createCartItem'? A string gets Type 'string' does not satisfy the constraint '(...args: any) => any'. (...args: any) => any is how TypeScript writes "any function", and ReturnType only takes those. In both error cases, the query shows any: TypeScript gave up on the type.

These two helpers are most useful when you wrap a function: you write a new function that does something extra, then calls the original. Here, a shop logs every coupon a shopper tries:

A wrapper that keeps applyCoupon's types (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
function applyCoupon(total: number, code: string) {  return code === "SAVE5" ? total - 5 : total;} function trackCoupon(  ...args: Parameters<typeof applyCoupon>): ReturnType<typeof applyCoupon> {  console.log("Coupon tried:", args[1]);  return applyCoupon(...args);} console.log(trackCoupon(20, "SAVE5"));trackCoupon(20);

Lines 1 to 3 apply a coupon: SAVE5 takes 5 off the total. Lines 5 to 10 wrap it. Line 6 collects every argument into the list args, typed Parameters<typeof applyCoupon>, so args is [total: number, code: string]. Line 7 says trackCoupon returns what applyCoupon returns, a number. Line 8 logs the code, and line 9 spreads args into applyCoupon.

Line 12 prints Coupon tried: SAVE5 and then 15. Line 13 forgets the code, and TypeScript marks it on purpose: Expected 2 arguments, but got 1, the same check a call to applyCoupon gets. It still runs and prints Coupon tried: undefined. If applyCoupon later takes a third argument, trackCoupon follows without an edit.

Unwrapping promises with Awaited

An async function always returns a promise: a value that arrives later (see Promises and async and await). So ReturnType of an async function is a promise type, not the data inside it. Press the buttons to name a loaded cart three ways:

Playground: name the loaded cart
The type of a loaded cartPop out in the code editor (opens in a new tab)TypeScript
async function fetchCart() {  return { items: ["Tea", "Cake"], total: 7 };} type Cart = ReturnType<typeof fetchCart>;//   ^? function cartSummary(cart: Cart) {  return `${cart.items.length} items, $${cart.total}`;} fetchCart().then((cart) => console.log(cartSummary(cart)));
Console output2 items, $7
What happens
TypeScript3 errors: lines 9 and 12
Try it yourself

fetchCart is async, so it returns a promise, and so does ReturnType: the answer after ^? starts with Promise<. Line 9 reads cart.items from a promise: Property 'items' does not exist on type 'Promise<. Line 12 passes the real cart where a promise is expected.

TypeScript checks the code for real, and the output is what the code prints, even with errors. Each button changes only line 5. Reset goes back to ReturnType only.

With ReturnType only, the query on line 6 shows Promise<{ items: string[]; total: number; }>. Line 9 gets Property 'items' does not exist on type 'Promise<…>' (and the same for total): a promise has no items, only the cart inside it does. Line 12 gets an error too, because .then hands cartSummary the real cart where it expects a promise.

Awaited of ReturnType unwraps the promise, and every error is gone. Awaited of the function unwraps nothing: typeof fetchCart is the function, not a promise, so Awaited gives it back as it is, and lines 9 and 12 get errors again. In every case the code prints 2 items, $7, because the types are removed before it runs.

Awaited follows the same rules as await. Like UnwrapAll from the infer lesson, it keeps unwrapping, and it leaves other types alone:

How Awaited unwrapsPop out in the code editor (opens in a new tab)TypeScript
type Nested = Awaited<Promise<Promise<number>>>;//   ^?type Plain = Awaited<string>;//   ^?type Mixed = Awaited<boolean | Promise<number>>;//   ^?

Line 1 is a promise of a promise of a number. The query on line 2 shows number: Awaited unwraps until no promise is left. Line 3 gives it string, which isn't a promise, so the query on line 4 shows string. Line 5 is a union, and Awaited works on each member: boolean stays, and Promise<number> becomes number. The query on line 6 shows number | boolean.

A crate labelled Promise holds a smaller crate labelled Promise, which holds a card number. An arrow labelled Awaited leads to a single card number on its own.numberPromisePromisea promise inside a promiseAwaitednumberthe value inside
Promise<Promise<number>> is a promise inside a promise. Awaited opens both and gives the number inside.
Real-life analogyA parcel inside a parcel

A friend sends a gift in a box, packed inside a bigger box. You open the big box, then the small one, and stop when you reach the gift. If they hand you the gift with no box at all, there's nothing to open.

In real life: A parcel with another parcel inside
In TypeScript: Promise<Promise<number>>
In real life: You open boxes until you reach the gift
In TypeScript: Awaited unwraps until it reaches a type that isn't a promise
In real life: The gift itself
In TypeScript: number
In real life: A gift handed to you without a box
In TypeScript: Awaited<string> is just string

For a variable, you rarely write Awaited yourself, because await already gives the unwrapped type:

await gives the unwrapped typePop out in the code editor (opens in a new tab)TypeScript
async function fetchCart() {  return { items: ["Tea", "Cake"], total: 7 };} async function showCart() {  const cart = await fetchCart();  //    ^?  console.log(`${cart.items.length} items`);}showCart();

Line 6 waits for fetchCart's promise. The query on line 7 shows cart is the cart itself, { items: string[]; total: number; }. Line 8 prints 2 items. You need Awaited when you want the name of that type without awaiting: for a function's parameter, like cartSummary(cart: Cart) in the playground, or for a variable you fill in later.

Shipping quotes on a checkout page

A checkout page asks a server what shipping costs. The function that asks is written once, and the page names its types from it:

Types named from one shipping functionPop out in the code editor (opens in a new tab)TypeScript
type Speed = "standard" | "express" | "drone"; async function quoteShipping(speed: Speed, weightKg: number) {  const pricePerKg = { standard: 3, express: 8, drone: 15 };  return { speed, price: pricePerKg[speed] * weightKg, days: speed === "standard" ? 4 : 2 };} type Quote = Awaited<ReturnType<typeof quoteShipping>>;//   ^?type OfferedSpeed = Exclude<Parameters<typeof quoteShipping>[0], "drone">;//   ^? function showQuote(quote: Quote) {  console.log(`${quote.speed}: $${quote.price} in ${quote.days} days`);} const offered: OfferedSpeed[] = ["standard", "express"];for (const speed of offered) {  quoteShipping(speed, 2).then(showQuote);}

Line 1 lists three delivery speeds. Lines 3 to 6 ask for a quote: line 4 has a price per kilogram for each speed, and line 5 returns the speed, the price for the weight, and the days it takes. It's async, as a real request to a server would be.

Line 8 names the quote with Awaited of ReturnType, and the query on line 9 shows its three properties. Line 10 takes the first parameter's type, Speed, and excludes "drone", because this shop doesn't deliver by drone yet. The query on line 11 shows "standard" | "express".

Lines 13 to 15 print a quote. Line 17 lists the speeds the page offers; "drone" there would be an error. Lines 18 to 20 ask for a 2-kilogram quote at each speed. The code prints standard: $6 in 4 days and then express: $16 in 2 days. No type was written twice: add a speed or a property to quoteShipping, and Quote and OfferedSpeed follow.

The table below sums up the six helpers. Read each row as: the helper, what you give it, and what you get back.

The six helpers in this lesson
The helperWhat you give itWhat you get back
Exclude<Union, Remove>A union, and the members to take outThe union without the members that fit Remove
Extract<Union, Keep>A union, and the members to keepOnly the members that fit Keep
NonNullable<T>Any typeT without null and undefined
ReturnType<typeof f>A function's typeThe type of what the function returns
Parameters<typeof f>A function's typeA tuple of what the function takes
Awaited<T>A promise type, or any typeThe value inside the promise, unwrapped all the way
Error or fine?
  • type Item = Exclude<"tea" | "cake", "cake">; const item: Item = "tea";
  • type Item = Extract<"tea" | "cake", "cake">; const item: Item = "tea";
  • type Result = ReturnType<string>;
  • function total() { return 7; } type Total = ReturnType<total>;
  • function total() { return 7; } const sum: ReturnType<typeof total> = 9;
  • const coupon: NonNullable<string | null> = null;
  • type Drink = Exclude<"tea" | "cake", "coffee">;
  • const total: Awaited<Promise<number>> = 7;
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

Exclude and Omit both take something out, so they're easy to swap. Omit (from the Partial, Pick, Omit & friends lesson) takes properties out of an object type:

Omit removes a property, Exclude removes nothing herePop out in the code editor (opens in a new tab)TypeScript
type Order = { id: number; total: number; coupon: string | null }; type WithoutCoupon = Omit<Order, "coupon">;//   ^?type NothingRemoved = Exclude<Order, "coupon">;//   ^?

Line 1 describes an order. Line 3 uses Omit to take out the coupon property, and the query on line 4 shows the id and total that are left. Line 5 tries Exclude instead. Order isn't a union of names; it's one object type, and it doesn't fit "coupon", so nothing is taken out. The query on line 6 shows all three properties, and no error warns you.

The table below compares them. Read each row as: what you write, what it works on, and what you get.

Exclude or Omit?
When you writeWhat it works onWhat you get
Exclude<OrderStatus, "cancelled">A union: the members of a type like "paid" | "cancelled".The union without "cancelled".
Omit<Order, "coupon">An object type: its properties.The object type without its coupon property.
Exclude<Order, "coupon">An object type, which is one member, not a union of keys.Order unchanged, with no error. Nothing in it fits "coupon".

NonNullable only works on the type you give it, not on the properties inside an object:

NonNullable doesn't reach inside an objectPop out in the code editor (opens in a new tab)TypeScript
type Profile = { name: string; nickname: string | null }; type Loaded = NonNullable<Profile | null>;//   ^?type Nickname = Loaded["nickname"];//   ^?

Line 3 takes the null out of Profile | null, and the query on line 4 shows a profile. Line 5 reads its nickname property's type, and the query on line 6 still shows string | null.

  • "Exclude tells me when I misspell a member." No. Exclude<OrderStatus, "canceled"> takes out nothing and reports nothing. Check the result with ^? or by hovering.
  • "ReturnType<createCartItem> works." No. A function's name is a value. In a type, write typeof createCartItem.
  • "ReturnType of an async function is its data." No. It's a promise of the data. Wrap it: Awaited<ReturnType<typeof fetchCart>>.
  • "NonNullable removes null everywhere inside a type." No. It removes null and undefined from the type itself, not from its properties.
  • "Exclude and Omit do the same job." No. Exclude filters a union. Omit removes properties from an object type.
  • "Awaited waits for the promise." No. Like every type, it's removed before the code runs. It only names the type the promise finishes with; await does the waiting.

Practice exercises

Each exercise is TypeScript. To change the starter code, press Edit & run: the errors and ^? answers update as you type, and the console shows what it prints. Try a guess first, then use the hints.

Exercise 1 · Warm-upRead what Exclude leaves

What is ShopPayment? Guess first. Then point at it, or press Edit & run and add a // ^? line under it.

Starter codePop out in the code editor (opens in a new tab)TypeScript
type Payment = "card" | "cash" | "voucher" | "crypto";type ShopPayment = Exclude<Payment, "voucher" | "crypto">;

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

    Exercise 2 · Warm-upFind the line TypeScript marks

    Which line will TypeScript mark? Then fix it.

    Starter codePop out in the code editor (opens in a new tab)TypeScript
    function loadUser(id: number) {
      return { id, name: "Asha" };
    }
    
    type User = ReturnType<loadUser>;

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

      Exercise 3 · PracticeAccept only remove events

      logRemove accepts every event, so line 12 logs Removed Tea for an add event. Change line 5 so it only accepts remove events. Which line does TypeScript mark then?

      Starter codePop out in the code editor (opens in a new tab)TypeScript
      type CartEvent =  | { type: "add"; name: string; price: number }  | { type: "remove"; name: string }; type RemoveEvent = CartEvent; function logRemove(event: RemoveEvent) {  console.log(`Removed ${event.name}`);} logRemove({ type: "remove", name: "Cake" });logRemove({ type: "add", name: "Tea", price: 3 });

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

        Exercise 4 · PracticeKeep null out of a greeting

        TypeScript marks line 4: the nickname might be null. Change only the parameter's type on line 3, then fix the call. What does the fixed code print?

        Starter codePop out in the code editor (opens in a new tab)TypeScript
        type Profile = { name: string; nickname: string | null }; function greet(nickname: Profile["nickname"]) {  console.log(`Hi, ${nickname.toUpperCase()}!`);} const asha: Profile = { name: "Asha", nickname: "Ash" };greet(asha.nickname);

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

          Exercise 5 · ChallengeName one loaded product

          Product on line 5 should be one product, but it's a promise of a list. Fix line 5 so the errors go away. What does the code print?

          Starter codePop out in the code editor (opens in a new tab)TypeScript
          async function fetchProducts(category: "tea" | "cake") {  return [{ name: "Green tea", price: 3, category }];} type Product = ReturnType<typeof fetchProducts>; function label(product: Product) {  return `${product.name}: $${product.price}`;} fetchProducts("tea").then((products) => console.log(label(products[0])));

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

            Exercise 6 · ChallengeWrap a function and keep its types

            withLog uses any, so nothing is checked, and TypeScript marks line 7: A spread argument must either have a tuple type or be passed to a rest parameter. Replace both anys with types read from applyCoupon. What is the last thing the code prints?

            Starter codePop out in the code editor (opens in a new tab)TypeScript
            function applyCoupon(total: number, code: string) {  return code === "SAVE5" ? total - 5 : total;} function withLog(...args: any[]): any {  console.log("applyCoupon", args.join(", "));  return applyCoupon(...args);} const total = withLog(20, "SAVE5");console.log(total);

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

              Check your understanding

              For each question, work out the helper's result one step at a time, from the inside out. The quiz shows its code without the checker, so the error list doesn't give the answers away.

              The ReturnType, Awaited & more quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What is OnSale in this menu code?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type Menu = "tea" | "cake" | "pie";
                type OnSale = Extract<Menu, "cake" | "coffee">;

                Choose an answer to see the explanation.

              2. Question 2 of 7What is Loaded for this async function?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                async function loadCart() {
                  return { total: 7 };
                }
                
                type Loaded = ReturnType<typeof loadCart>;

                Choose an answer to see the explanation.

              3. Question 3 of 7What does TypeScript say about this misspelled Exclude, and what is Open?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type Status = "paid" | "shipped" | "cancelled";
                type Open = Exclude<Status, "canceled">;

                Choose an answer to see the explanation.

              4. Question 4 of 7Which line does TypeScript flag in this coupon code?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                function applyCoupon(total: number, code: string) {
                  return code === "SAVE5" ? total - 5 : total;
                }
                
                type CouponArgs = Parameters<applyCoupon>;

                Choose an answer to see the explanation.

              5. Question 5 of 7In Loaded, what is the type of nickname?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type Profile = { name: string; nickname: string | null };
                type Loaded = NonNullable<Profile | null>;

                Choose an answer to see the explanation.

              6. Question 6 of 7What is Result after Awaited unwraps this union?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type Result = Awaited<Promise<Promise<number>> | string>;

                Choose an answer to see the explanation.

              7. Question 7 of 7Which helper gives the types of a function's arguments, as a list?

                Choose an answer to see the explanation.

              Key takeaways

              • Exclude<Union, Remove> takes out the members that fit Remove. Extract<Union, Keep> keeps only the members that fit Keep.
              • Neither one warns you when the second type matches nothing, so check the result.
              • NonNullable<T> removes null and undefined from T itself, not from its properties.
              • ReturnType and Parameters read a function's type, which you get with typeof: ReturnType<typeof createCartItem>.
              • Awaited unwraps promises all the way. For an async function, write Awaited<ReturnType<typeof fetchCart>>.
              • Types made this way follow the type or function they come from, so you never keep two copies in step by hand.

              Remember the one-liner.
              Utility types build new types from the ones you have: filter a union, drop null, read a function, or unwrap a promise.

              Coming next: Writing your own utility types. It rebuilds Partial and Pick from scratch, then writes helpers of your own, like a DeepPartial for settings with objects inside.

              CompleteFrontend Clear concepts. Working examples.