Skip to content
tezvyn:
Menu

Create TerminalStatus from Status using Exclude or Extract

Source: typescriptlang.orgMediumHow cards are made

This tests conditional type mechanics and whitelist versus blacklist thinking. Extract selects matches by assignability; Exclude removes others. Prefer Extract when the desired set is explicit. Red flag: claiming they are equivalent or only knowing Exclude.

What's really being asked

This question evaluates your familiarity with TypeScript's built-in conditional utility types and whether you can reason about set operations on unions. The interviewer wants to see that you know Extract and Exclude are not synonyms but duals: one keeps types assignable to a pattern, the other removes them. At the senior level, they also care if you can articulate maintainability trade-offs, such as which approach survives better when the source union changes.

The full answer

First, write the implementation using Extract: type TerminalStatus = Extract<Status, 'success' | 'error'>. Second, explain that Extract<T, U> keeps members of T that are assignable to U, which is a whitelist. Third, show the Exclude alternative: type TerminalStatus = Exclude<Status, 'pending' | 'idle'>, noting that Exclude<T, U> removes members of T that are assignable to U, which is a blacklist. Fourth, justify the choice: prefer Extract when the terminal set is small and semantically stable, because adding a new non-terminal status to Status will not leak into TerminalStatus. Prefer Exclude when the unwanted set is smaller or volatile. Fifth, mention that both are implemented with conditional types under the hood, for example Extract is roughly T extends U ? T : never.

The mistakes people make

Answering with only Exclude because you have never used Extract. Saying the two utilities are interchangeable; they produce the same result here but read differently to future maintainers. Proposing manual conditional types without acknowledging the built-ins. Suggesting runtime methods like filter or array operations instead of type-level transforms. Forgetting that the second argument to Extract or Exclude can itself be a union.

What usually comes next

How would you derive TerminalStatus dynamically if the terminal criteria were more complex, such as all Status values except those ending in ing? What happens if Status is refactored into an enum or a const object with as const? Can you implement Extract or Exclude yourself using infer? How do these utilities interact with distributive conditional types when T is a union?

A concrete example

Imagine a payment state machine with Status = 'pending' | 'processing' | 'success' | 'error' | 'refunded'. A dashboard widget only cares about final outcomes. Using Extract<Status, 'success' | 'error' | 'refunded'> makes the intent obvious: these are the terminal states. If the product later adds 'disputed', the widget's type remains correct without edits. If you had used Exclude<Status, 'pending' | 'processing'> instead, the new 'disputed' state would silently appear in TerminalStatus, which might be a bug if disputes need separate handling.

Interview question

Given a Status union that gains new intermediate values over time, which type definition ensures TerminalStatus remains correct without future modifications?

  • a.Extract<Status, 'success' | 'error'>Correct
  • b.Status extends 'pending' | 'idle' ? never : Status
  • c.Pick<Status, 'success' | 'error'>
  • d.Exclude<Status, 'pending' | 'idle'>
Why?

Extract performs a whitelist that only admits the explicitly named terminal states, so newly added intermediate statuses are automatically omitted. Exclude only removes the currently known non-terminal states, meaning any future intermediate status would silently appear in TerminalStatus.

Just read this? Test yourself on what you have been reading.

Read the original → typescriptlang.org

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on typescript — each one lists the topics its interview covers.

See open roles