This error means TypeScript’s static analysis has determined that you’re trying to access a property or method on an object that it doesn’t believe exists on that object’s declared type, and it’s stopping compilation to prevent a runtime error.

Common Causes and Fixes for TS2339: Property Does Not Exist on Type

This is one of the most frequent TypeScript errors, often stemming from subtle mismatches between your code’s assumptions and the actual types involved. Here are the most common culprits and how to fix them:

  1. Incorrect Type Assertion or Union Type: You might have an object that could be one of several types, and you’re trying to access a property that only exists on some of those types, without properly narrowing down the type first.

    • Diagnosis: Use console.log(typeof myObject) or console.log(myObject) to inspect the actual type at runtime. If it’s a union, check your if conditions or type guards.

    • Fix: Implement a type guard or a more specific check. For example, if myObject could be TypeA or TypeB, and propC only exists on TypeA:

      interface TypeA {
        propA: string;
        propC: number;
      }
      
      interface TypeB {
        propB: boolean;
      }
      
      let myObject: TypeA | TypeB = { propA: "hello" }; // Or it could be TypeB
      
      // Incorrect:
      // console.log(myObject.propC); // TS2339
      
      // Correct:
      if ('propC' in myObject) {
        console.log(myObject.propC); // TS knows propC exists here
      }
      // Or if you know it's TypeA:
      if ((myObject as TypeA).propA !== undefined) { // A more robust check for TypeA
        console.log((myObject as TypeA).propC);
      }
      
    • Why it works: TypeScript’s control flow analysis understands that within the if block, myObject is guaranteed to be TypeA (or at least have propC), thus satisfying the type checker.

  2. External Libraries with Missing or Incorrect Type Definitions: You’re using a JavaScript library that doesn’t have accompanying TypeScript definition files (.d.ts) or the definitions are outdated or wrong.

    • Diagnosis: Check if the library has an official @types/library-name package available on npm. If not, or if it’s old, the types might be incomplete.
    • Fix:
      • Install the official types if they exist: npm install --save-dev @types/library-name

      • If types are missing or insufficient, you can declare the missing property in a declaration file (e.g., src/types/global.d.ts):

        // src/types/global.d.ts
        declare module 'library-name' {
          // If the whole module is missing types
          const someExport: any;
          export { someExport };
        
          // If a specific object/function is missing a property
          export function someFunction(arg: any): any;
          // Or if you know the shape of an exported object
          export interface SomeExportedObject {
            existingProp: string;
            missingProp: number; // Declare the missing one
          }
        }
        

        Or, more generally, for an object where you know a property exists:

        // src/types/global.d.ts
        interface Window { // Example for browser global
          myCustomGlobal: {
            someProperty: string;
          };
        }
        
      • Alternatively, cast the object to any (use sparingly): (myObject as any).missingProperty

    • Why it works: Declaration files tell TypeScript about the shape of JavaScript code. By adding the missing declaration, you’re informing TypeScript that the property does exist, satisfying its static analysis. Casting to any bypasses type checking entirely for that expression.
  3. Typo in Property Name: A simple, but common, mistake. You’ve mistyped the name of the property you’re trying to access.

    • Diagnosis: Carefully compare the property name in your code (myObject.propertName) with the actual name defined in the type definition or documentation (myObject.propertyName).
    • Fix: Correct the typo. For example, change myObject.usrname to myObject.username.
    • Why it works: TypeScript’s compiler will now find a matching property definition for the corrected name.
  4. Object is null or undefined: The variable holding the object might be null or undefined at runtime, and the property doesn’t exist on null or undefined.

    • Diagnosis: Use console.log(myObject) right before the line causing the error. If it prints null or undefined, that’s your issue.

    • Fix: Add a check to ensure the object is not null or undefined before accessing its properties:

      let myObject: MyType | null = getObjectPossiblyNull();
      
      // Incorrect:
      // console.log(myObject.someProperty); // TS2339 if myObject is null
      
      // Correct:
      if (myObject !== null && myObject !== undefined) {
        console.log(myObject.someProperty);
      }
      // Or using optional chaining (modern JS/TS):
      console.log(myObject?.someProperty); // This will be undefined if myObject is null/undefined
      
    • Why it works: The if condition ensures that myObject is a valid object before its properties are accessed. Optional chaining (?.) provides a concise way to safely access nested properties, returning undefined if any part of the chain is null or undefined.

  5. Incorrect this Context: Within a class method, you might be trying to access a property of the class instance (this.propertyName), but the method is being called with an incorrect this context.

    • Diagnosis: Ensure that methods using this are correctly bound, especially when passed as callbacks (e.g., to event listeners or setTimeout).
    • Fix:
      • Use arrow functions for class methods:

        class MyClass {
          myProperty: string = "hello";
        
          // Use arrow function to preserve 'this' context
          myMethod = () => {
            console.log(this.myProperty);
          }
        }
        
      • Bind this in the constructor:

        class MyClass {
          myProperty: string = "hello";
        
          constructor() {
            this.myMethod = this.myMethod.bind(this);
          }
        
          myMethod() {
            console.log(this.myProperty);
          }
        }
        
      • Bind when passing the method as a callback: element.addEventListener('click', this.myMethod.bind(this));

    • Why it works: Arrow functions lexically bind this, meaning this always refers to the this of the enclosing scope (the class instance). Explicitly binding in the constructor or when passing the method ensures that this inside myMethod correctly refers to the class instance.
  6. Module Resolution Issues: TypeScript can’t find the module or the specific export you’re trying to import, leading it to believe the imported object doesn’t have the expected properties.

    • Diagnosis: Check your import statements. Ensure the path is correct and that the exported name matches what you’re importing. Verify your tsconfig.json moduleResolution setting (usually node or bundler).
    • Fix: Correct the import path or the exported name. For example, if my-module.ts exports myFunction as export function myFunction() {}, you must import it as import { myFunction } from './my-module';. If you’re importing a default export, use import MyDefault from './my-module';.
    • Why it works: Correct imports ensure that TypeScript knows precisely which object or function is being brought into scope and can correctly apply its type information.

If you’ve addressed all these, the next error you’ll likely encounter is a type mismatch on the value assigned to the property you just fixed, or a similar TS2339 on a different property of the same object.

Want structured learning?

Take the full Typescript course →