TS2339Semantic Error
Since TS 1.0

Fix TS2339: Property Does Not Exist on Type

Learn why TypeScript throws TS2339 when accessing a property that doesn't exist on a type, and how to fix missing property errors.

error TS2339: Property 'X' does not exist on type 'Y'

What This Error Means

TS2339 means you're trying to read or write a property that TypeScript doesn't know about on the given type. TypeScript tracks the shape of every value, and when you access .something that isn't part of that shape, it raises this error.

This is one of the biggest advantages of TypeScript over plain JavaScript. In JavaScript, accessing a missing property silently returns undefined and can cause subtle bugs. TypeScript catches these mistakes before your code runs.

// The general shape of the error:
// Property 'middleName' does not exist on type 'User'.
//           ~~~~~~~~~~                          ~~~~
//           property you accessed               the type TS knows about

Common Causes

1. Typo in the Property Name

The most common cause is a simple spelling mistake. TypeScript error 2551 ("Did you mean...?") sometimes appears instead, but when the name isn't close enough to any known property, you get 2339.

// ❌ Broken
interface Product {
  name: string
  price: number
  inStock: boolean
}
 
function displayProduct(product: Product) {
  console.log(product.naem) // Error: Property 'naem' does not exist on type 'Product'.
}
// ✅ Fixed — correct the typo
function displayProduct(product: Product) {
  console.log(product.name)
}

2. Property Not Declared in the Interface

You're accessing a property that genuinely isn't part of the type definition. Maybe the interface is out of date, or the property was added to the API response but not to your types.

// ❌ Broken
interface ApiResponse {
  data: unknown
  status: number
}
 
function handleResponse(response: ApiResponse) {
  console.log(response.headers)
  //                   ~~~~~~~ Error: Property 'headers' does not exist on type 'ApiResponse'.
}
// ✅ Fixed — add the property to the interface
interface ApiResponse {
  data: unknown
  status: number
  headers: Record<string, string>
}
 
function handleResponse(response: ApiResponse) {
  console.log(response.headers)
}

3. Accessing a Property on a Union Type

When a variable has a union type, TypeScript only allows access to properties shared by all members of the union. Properties unique to one member require narrowing first.

// ❌ Broken
interface CreditCardPayment {
  type: "credit_card"
  cardNumber: string
  expiryDate: string
}
 
interface BankTransferPayment {
  type: "bank_transfer"
  accountNumber: string
  routingNumber: string
}
 
type Payment = CreditCardPayment | BankTransferPayment
 
function processPayment(payment: Payment) {
  console.log(payment.cardNumber)
  //                  ~~~~~~~~~~ Error: Property 'cardNumber' does not exist on type 'Payment'.
}
// ✅ Fixed — narrow the union with a type guard
function processPayment(payment: Payment) {
  if (payment.type === "credit_card") {
    console.log(payment.cardNumber) // OK — TypeScript knows this is CreditCardPayment
  } else {
    console.log(payment.accountNumber) // OK — TypeScript knows this is BankTransferPayment
  }
}

4. Working with document.getElementById and DOM Elements

DOM methods often return a base type like HTMLElement that doesn't have properties specific to <input>, <canvas>, or other element types.

// ❌ Broken
const searchInput = document.getElementById("search")
console.log(searchInput.value)
//                      ~~~~~ Error: Property 'value' does not exist on type 'HTMLElement'.
// ✅ Fixed — narrow to the specific element type
const searchInput = document.getElementById("search") as HTMLInputElement | null
 
if (searchInput) {
  console.log(searchInput.value) // OK — value exists on HTMLInputElement
}

How to Fix It

  1. Check for typos. Compare the property name you wrote against the type definition letter by letter. IDE autocomplete helps prevent this.

  2. Inspect the type definition. Hover over the variable in your editor to see what type TypeScript has inferred. If the type is too narrow or a base type, the property won't be available.

  3. Add the property to the type if it legitimately belongs there. Update your interface or type alias.

  4. Narrow the type if you're dealing with a union. Use discriminated unions, typeof checks, in operator checks, or custom type guards.

// Using the 'in' operator to narrow
function getLabel(item: Product | Category) {
  if ("price" in item) {
    return `${item.name} — $${item.price}` // Product
  }
  return item.categoryName // Category
}
  1. Use type assertions sparingly. If you know more than TypeScript does (e.g., a DOM element's specific type), a targeted assertion is acceptable. Avoid casting to any.

FAQ

What causes TS2339?

TS2339 fires when you try to access a property or method on a value whose type doesn't include that property. TypeScript only allows access to properties it knows about at compile time.

This is different from JavaScript, where accessing a missing property just gives you undefined. TypeScript's approach catches bugs early: misspelled property names, outdated interfaces, and incorrect assumptions about data shapes all surface as TS2339 at compile time rather than as mysterious undefined values at runtime.

How do I fix "property does not exist on type"?

The fix depends on why the property is missing:

If you're working with data from an external API and the shape changes frequently, consider using a validation library like Zod to define and validate the shape at runtime, which also generates the TypeScript types for you.

Why does TS2339 happen with union types?

TypeScript uses a concept called "type narrowing" with unions. When you have A | B, only properties that exist on both A and B are safe to access directly. That is because at runtime, the value could be either type.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number }
 
function getArea(shape: Shape) {
  // shape.radius — Error! Not all shapes have radius
  // shape.width  — Error! Not all shapes have width
 
  // Narrow first:
  if (shape.kind === "circle") {
    return Math.PI * shape.radius ** 2 // OK
  }
  return shape.width * shape.height // OK
}

The kind property works as a discriminant because it exists on both members of the union. Once you check its value, TypeScript narrows the type automatically.

Related Errors

Related Concepts

Share this reference

Stay Updated

Get the latest TypeScript tips, tutorials, and course updates delivered straight to your inbox.

No spam, unsubscribe at any time.