What is TS2339?
TS2339 occurs when you try to access a property that TypeScript doesn't recognize on a given type. This is one of TypeScript's most helpful errors as it catches typos, missing properties, and incorrect assumptions about object shapes at compile time.
// Example of TS2339 error interface User { name: string; age: number; } const user: User = { name: "John", age: 30 }; console.log(user.email); // Error: Property 'email' does not exist on type 'User'
Common Causes
1. Typo in Property Name
The most common cause is simply misspelling a property name:
interface Config { apiEndpoint: string; timeout: number; } const config: Config = { apiEndpoint: "/api", timeout: 5000 }; // โ Wrong - typo console.log(config.apiEndPoint); // Error! (capital P) // โ Correct console.log(config.apiEndpoint);
2. Missing Property in Interface
The property exists on the object but not in the type definition:
// โ Interface is incomplete interface User { name: string; } const user: User = { name: "John" }; console.log(user.email); // Error! // โ Add the missing property to interface interface User { name: string; email?: string; // Optional property }
3. Accessing Properties on Union Types
When a variable can be multiple types:
interface Dog { bark(): void; } interface Cat { meow(): void; } function makeSound(animal: Dog | Cat) { animal.bark(); // Error! Cat doesn't have bark() } // โ Use type narrowing function makeSound(animal: Dog | Cat) { if ('bark' in animal) { animal.bark(); // OK - TypeScript knows it's a Dog } else { animal.meow(); // OK - TypeScript knows it's a Cat } }
4. Object from External Source (API, JSON)
Data from external sources isn't type-checked:
// โ Unknown shape from API const data = await fetch('/api/user').then(r => r.json()); console.log(data.name); // Error! 'data' is 'any' or unknown // โ Define and assert the type interface UserResponse { name: string; email: string; } const data: UserResponse = await fetch('/api/user').then(r => r.json()); console.log(data.name); // OK
Solutions
1. Add Property to Interface
interface User { name: string; email: string; // Add the missing property phone?: string; // Use ? for optional properties }
2. Use Type Narrowing
function process(input: string | string[]) { if (typeof input === 'string') { console.log(input.toUpperCase()); // OK } else { console.log(input.length); // OK - array } }
3. Use Optional Chaining
interface User { name: string; address?: { city: string; }; } const user: User = { name: "John" }; console.log(user.address?.city); // undefined (no error)
4. Extend Existing Types
// Extending an interface interface BaseUser { name: string; } interface AdminUser extends BaseUser { adminLevel: number; } const admin: AdminUser = { name: "Admin", adminLevel: 1 }; console.log(admin.adminLevel); // OK
5. Index Signature (Dynamic Properties)
// When you need dynamic property names interface Config { [key: string]: string | number; } const config: Config = {}; config.anyProperty = "value"; // OK config.anotherOne = 123; // OK
๐ก Pro Tip
Always prefer specific interfaces over index signatures when possible. Index signatures sacrifice type safety for flexibility.
Real-World Examples
Working with DOM Elements
const element = document.getElementById('myInput'); // โ Error - element might be null, and it's HTMLElement, not HTMLInputElement console.log(element.value); // Error! // โ Type narrowing with assertion const input = document.getElementById('myInput') as HTMLInputElement; if (input) { console.log(input.value); // OK }
Event Handling
// โ Generic Event doesn't have 'target.value' function handleChange(e: Event) { console.log(e.target.value); // Error! } // โ Use specific event type function handleChange(e: React.ChangeEvent<HTMLInputElement>) { console.log(e.target.value); // OK }
โ ๏ธ Avoid This
Don't use (obj as any).property to silence this error. It defeats the purpose of TypeScript and can lead to runtime errors.
Summary
- TS2339 protects you from accessing non-existent properties
- Check for typos in property names first
- Update interfaces to include missing properties
- Use type narrowing for union types
- Use optional chaining (?.) for possibly undefined properties
- Avoid using
anyto bypass the error