TS2739Semantic Error
Since TS 3.2

Fix TS2739: Type Is Missing the Following Properties

Learn why TypeScript throws TS2739 when a value is missing several required properties of its target type, and how to fix it without casting.

error TS2739: Type 'X' is missing the following properties from type 'Y': a, b, c

What This Error Means

TypeScript error TS2739 means a value does not have all the properties its target type requires — and that more than one property is missing. The compiler compares the two shapes, finds the gaps, and lists them by name:

const profile: UserProfile = fromApi
//    ~~~~~~~
// Type 'UserProfileDto' is missing the following properties
// from type 'UserProfile': displayName, createdAt

This is TypeScript's structural type system doing its job. A type is not a label you attach to a value; it is a contract about which properties the value has. UserProfileDto and UserProfile are unrelated as far as the compiler is concerned — all that matters is that the DTO lacks displayName and createdAt, so it cannot stand in for a UserProfile.

The number of missing properties decides which code you see. One missing property is TS2741 (Property 'X' is missing in type 'Y' but required in type 'Z'), two to five are listed by name as TS2739, and from six onwards the compiler truncates the list and switches to TS2740 (…: a, b, c, d, and 2 more). Same check, same fixes — only the wording of the report changes.

Common Causes

1. Starting From an Empty Object Literal

The most common source is a variable that is annotated with its final type but initialised empty, with the intention of filling it in later. The annotation is a promise the empty literal cannot keep.

interface ChartOptions {
  width: number
  height: number
  theme: "light" | "dark"
}
 
// ❌ Broken
const chartOptions: ChartOptions = {}
//    ~~~~~~~~~~~~
// Error: Type '{}' is missing the following properties from type
// 'ChartOptions': width, height, theme (TS2739)
chartOptions.width = 640
chartOptions.height = 360

The assignments on the following lines do not help: TypeScript checks the initialiser at the point of assignment, not what you intend to do with the variable afterwards.

// ✅ Fixed — "not configured yet" is a real state, so put it in the type
let chartOptions: ChartOptions | null = null
 
function configureChart(width: number, height: number) {
  chartOptions = { width, height, theme: "light" }
}

Now every reader of chartOptions is forced to handle the null case, which is exactly the bug the empty object was hiding.

2. Assigning a Partial API Payload to a Domain Type

Backend DTOs rarely match domain models one to one. Assigning one to the other works until the domain type grows a field the payload does not carry.

interface UserProfileDto {
  id: string
  email: string
}
 
interface UserProfile {
  id: string
  email: string
  displayName: string
  createdAt: Date
}
 
const fromApi: UserProfileDto = { id: "u_1842", email: "ada@example.com" }
 
// ❌ Broken
const profile: UserProfile = fromApi
//    ~~~~~~~
// Error: Type 'UserProfileDto' is missing the following properties from
// type 'UserProfile': displayName, createdAt (TS2739)
// ✅ Fixed — map the transport shape to the domain shape explicitly
function toUserProfile(dto: UserProfileDto): UserProfile {
  return {
    id: dto.id,
    email: dto.email,
    displayName: dto.email.split("@")[0],
    createdAt: new Date(),
  }
}
 
const profile: UserProfile = toUserProfile(fromApi)

A mapper function is more code than a cast, and it is the only version that still compiles when someone adds a fifth field to UserProfile: the error then points at toUserProfile, which is where the decision belongs.

3. Rendering a React Component Without Its Required Props

In JSX the props object is checked against the props type, so forgetting required props produces the same error — with the missing prop names listed.

interface InvoiceCardProps {
  invoiceId: string
  amount: number
  currency: string
  onOpen: () => void
}
 
function InvoiceCard({ invoiceId, amount, currency, onOpen }: InvoiceCardProps) {
  return (
    <button onClick={onOpen}>
      {invoiceId}: {amount} {currency}
    </button>
  )
}
 
export function InvoiceList() {
  // ❌ Broken
  return <InvoiceCard invoiceId="inv_42" />
  //      ~~~~~~~~~~~
  // Error: Type '{ invoiceId: string; }' is missing the following properties
  // from type 'InvoiceCardProps': amount, currency, onOpen (TS2739)
}
// ✅ Fixed — pass every required prop
export function InvoiceList() {
  return (
    <InvoiceCard
      invoiceId="inv_42"
      amount={128.5}
      currency="EUR"
      onOpen={() => console.log("open inv_42")}
    />
  )
}

If a prop is genuinely optional, say so in the props type (currency?: string) and give it a default in the component — do not force callers to pass a placeholder value.

4. Hand-Rolled Stubs That Implement Only Part of an Interface

Test doubles and in-memory fakes are the other classic case: the stub implements the one method the test exercises, and the compiler points out the rest of the contract.

interface Order {
  id: string
  total: number
}
 
interface OrderRepository {
  findById(id: string): Promise<Order | null>
  save(order: Order): Promise<void>
  delete(id: string): Promise<void>
}
 
// ❌ Broken
const fakeRepository: OrderRepository = {
  //  ~~~~~~~~~~~~~~
  // Error: Type '{ findById: () => Promise<{ id: string; total: number; }>; }'
  // is missing the following properties from type 'OrderRepository':
  // save, delete (TS2739)
  findById: async () => ({ id: "ord_1", total: 42 }),
}
// ✅ Fixed — either complete the stub...
const fakeRepository: OrderRepository = {
  findById: async () => ({ id: "ord_1", total: 42 }),
  save: async () => undefined,
  delete: async () => undefined,
}
 
// ✅ ...or narrow the contract the consumer actually needs
type OrderReader = Pick<OrderRepository, "findById">
 
const readOnlyRepository: OrderReader = {
  findById: async () => ({ id: "ord_1", total: 42 }),
}

The second fix is usually the better one: if a function only reads orders, typing its parameter as OrderReader makes every future stub smaller and documents the dependency honestly.

How to Fix It

  1. Read the property list in the message. TypeScript names every missing property. Nine times out of ten the fastest fix is to supply those properties with real values — the error is a genuine gap, not a type-system quirk.

  2. Model "not ready yet" with null, not with an empty object. A variable that will be filled in later is ChartOptions | null, never ChartOptions initialised to an empty literal. The union forces the rest of the code to handle the empty state instead of crashing at runtime on undefined.

  3. Convert between shapes with a mapper function. When the source is a DTO, a form value, or a third-party payload, write a toDomainType() function that fills in the missing fields. It gives you one place to change when either shape evolves.

  4. Relax the target type only when the properties really are optional. Mark them with ? (avatarUrl?: string), or accept Partial<T> in a function that updates rather than constructs. Use Pick<T, K> to ask for the narrow contract you actually consume.

  5. Do not silence it with a cast. fromApi as UserProfile or {} as ChartOptions compiles, but every later read of createdAt or theme gets undefined at runtime while the type says otherwise. If you must bridge an untyped boundary, validate the payload at that boundary and keep the rest of the code honestly typed.

FAQ

What causes TypeScript error TS2739?

TS2739 fires when you assign a value to something whose type requires properties the value does not have, and two or more of those properties are missing. TypeScript checks shapes structurally, so it does not matter whether the names of the two types look related — only whether every required property is present. The message lists the missing names, which makes the error unusually easy to act on. The usual sources are empty object literals, partial API payloads, React components rendered without required props, and incomplete test stubs.

How do I fix TS2739 when an empty object is assigned to a typed variable?

Stop annotating the empty literal with the full type. If the value is genuinely built in one place, construct it with every required property at once. If it is filled in later, type the variable as ChartOptions | null and start at null — the compiler then forces every reader to deal with the unconfigured state, which is the bug the empty object was hiding. Partial<ChartOptions> is the right type only while the value is still incomplete, and you should convert it to the full type in one checked step before handing it to code that expects all the fields.

What is the difference between TS2739, TS2740 and TS2741?

All three come from the same structural assignability check and differ only in how much detail fits in the message. TS2741 reports a single missing property (Property 'theme' is missing in type '…' but required in type 'ChartOptions'), TS2739 lists two to five missing properties by name, and TS2740 appears when more than five are missing and truncates the list with and N more. Seeing TS2740 instead of TS2739 therefore tells you the shape mismatch is large — often a completely different type, not a nearly-complete object. The fixes are identical: supply the properties, map between the shapes, or narrow the target type.

Related Errors

Practice This

Browse all TypeScript practice challenges to keep sharpening your type-level skills.

Related Concepts

Share this reference

Become a TypeScript Pro

Track your progress through 200+ hands-on challenges. Free, sign in with GitHub.

Or start solving right away: explore all TypeScript challenges