Skip to main content

Utility type required in TypeScript

Required is a TypeScript utility type that strips the ? from every optional property in T, making all of them required at compile time.

Theory

TL;DR

  • Think of a form where all fields were optional, then someone stamped "fill in everything" on each one
  • Main difference: removes ? from each property but keeps the type (string | undefined does NOT become string | undefined - it becomes just string)
  • Use after validation when you know all fields are present; keep Partial<T> while building the object
  • Compile-time only - no runtime effect
  • Opposite of Partial<T>

Quick example

ts
interface User { name: string; age?: number; // optional } type StrictUser = Required<User>; const user: StrictUser = { name: "Alice", age: 30 }; // works // const bad: StrictUser = { name: "Bob" }; // Error: age is missing

StrictUser now demands both name and age. Skip either one and TypeScript complains immediately.

Key difference

Required<T> only removes the ? modifier - it does not keep undefined in the type. So age?: number becomes age: number, not age: number | undefined. That matters when you write functions expecting a guaranteed value and want TypeScript to catch omissions at compile time, not at runtime.

When to use

  • Data collected as Partial<T>, then validated: use Required<T> as the return type of your validation function
  • API response after a successful request: you know the shape is complete
  • React component props with all defaults applied: pass Required<Props> to child components
  • Generating mock data: avoids unexpected undefined crashes in tests
  • After a type guard confirmed all fields exist: data is Required<FormData> reads cleanly

How the compiler handles this

TypeScript maps over every property descriptor in T. For each one marked as optional, it rewrites the descriptor to required. Under the hood this is a mapped type: { [K in keyof T]-?: T[K] }. The -? syntax strips the optional modifier. No JavaScript is emitted - purely a compile-time transformation.

Common mistakes

1. Expecting runtime enforcement

ts
// Required<T> does NOT check values at runtime const data = JSON.parse(response) as Required<User>; // bypasses all checks

TypeScript trusts the cast. Pair Required<T> with an actual type guard or a schema validator like Zod if you need runtime safety.

2. Thinking it handles nested optionals

The shallow behavior catches almost everyone the first time. You apply Required<T> expecting full coverage, then spend an hour debugging undefined three levels deep in a config object.

ts
type Nested = Required<{ a?: { b?: string } }>; // Result: { a: { b?: string } } // a is required, but b inside is still optional

For deep nesting, write a recursive type:

ts
type DeepRequired<T> = T extends object ? { [K in keyof T]-?: DeepRequired<T[K]> } : T;

3. Applying it to union types

ts
type Bad = Required<string | { a?: number }>; // Resolves unexpectedly - avoid Required on union types directly

If your type is a union, apply Required to each member separately or use Extract first.

4. Using it on primitives

Required<string> returns string unchanged. There is nothing to strip.

Real-world usage

  • React form handler: validateForm(data: FormData): data is Required<FormData> - type guard after checking all fields
  • Express middleware: Request<{}, {}, Required<CreateUserBody>> after body validation
  • React Query: Required<Omit<Response, 'data'>> on the success path
  • Zod: z.infer<typeof schema> & Required<PartialFields> for partial schemas
  • TanStack Table: column def merging with Required<Partial<ColumnDef>>

Follow-up questions

Q: What is the type of Required<{ a?: string }>?
A: { a: string }. The ? is removed and the type stays string, not string | undefined.

Q: Does Required<T> affect nested objects?
A: No. It only removes ? from top-level properties. Nested optional fields stay optional. Use a custom DeepRequired<T> for that.

Q: What is the difference between Required<T> and Omit<T, never>?
A: Same structural result, but Required<T> is the semantic choice. Omit<T, never> is a workaround and fails on non-object types.

Q: How do you make Required apply only to specific keys?
A: type RequiredSpecific<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>. This targets only the keys you name, leaving the rest untouched.

Q: Why does -? appear in mapped types?
A: It is a modifier syntax. +? adds the optional flag, -? strips it. Required<T> uses { [K in keyof T]-?: T[K] } internally.

Examples

Basic: making optional fields required

ts
interface Product { id: number; name: string; description?: string; price?: number; } type FullProduct = Required<Product>; const item: FullProduct = { id: 1, name: "Keyboard", description: "Mechanical", // now required price: 99, // now required }; // const broken: FullProduct = { id: 1, name: "Mouse" }; // Error: missing fields

Before Required<T>, you could create a Product without description or price. After, TypeScript blocks it at the type level.

Intermediate: form validation with a type guard

ts
interface FormData { email: string; phone?: string; address?: string; } function validateForm(data: FormData): data is Required<FormData> { return !!data.phone && !!data.address; } function submitUser(formData: FormData) { if (validateForm(formData)) { // TypeScript knows all fields are present inside this block console.log(`${formData.email} - ${formData.phone} - ${formData.address}`); } } const data: FormData = { email: "user@example.com", phone: "555-1234", address: "10 Main St", }; submitUser(data);

The type guard data is Required<FormData> narrows the type inside the if block. No casting needed. This pattern appears often in Next.js API routes after collecting and checking form input.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?