Home › JavaScript

Property 'x' does not exist on type 'Y' — how to fix

error TS2339: Property 'name' does not exist on type '{}'.
error TS2339: Property 'value' does not exist on type 'EventTarget'.
error TS2339: Property 'foo' does not exist on type 'Window & typeof globalThis'.

Quick fix: Give the value a proper type (interface User { name: string }, useState<User | null>(null)), narrow unions before access (if ('name' in obj)), cast DOM targets (e.target as HTMLInputElement), and extend globals with declare global { interface Window { foo: string } }. Avoid any unless you must.

What it means

TypeScript checks every property access against the static type of the value. If the type doesn't declare that property, it reports TS2339 — even if the property exists at runtime. The fix is to tell the compiler the real type, not to suppress the error.

Cause 1: the type was inferred as {} or too narrowly

const user = {};
user.name = 'Ada';        // TS2339: Property 'name' does not exist on type '{}'

Declare the shape:

interface User { name?: string; age?: number }
const user: User = {};
user.name = 'Ada';

// dynamic keys
const counts: Record<string, number> = {};
counts.apples = 3;

Cause 2: React useState without a type

const [user, setUser] = useState(null);   // type: null
user.name;                                // error (and type 'never' after a null check)
const [user, setUser] = useState<User | null>(null);
if (user) console.log(user.name);
const [items, setItems] = useState<Item[]>([]);   // [] alone infers never[]

Cause 3: union types not narrowed

type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number };

function area(s: Shape) {
  return s.radius;       // error: not on the 'square' member
}

Narrow first:

function area(s: Shape) {
  if (s.kind === 'circle') return Math.PI * s.radius ** 2;
  return s.size ** 2;
}
// or: if ('radius' in s) { ... }

Cause 4: DOM event targets and elements

e.target is typed as EventTarget, and querySelector returns Element, neither of which has .value:

input.addEventListener('input', e => {
  const v = (e.target as HTMLInputElement).value;
});

const el = document.querySelector<HTMLInputElement>('#email');
console.log(el?.value);

In React, type the handler: (e: React.ChangeEvent<HTMLInputElement>) => e.target.value (or use e.currentTarget).

Cause 5: adding properties to window or other globals

Create a .d.ts file included by your tsconfig.json (e.g. src/global.d.ts):

export {};
declare global {
  interface Window {
    dataLayer: unknown[];
  }
}

Same idea for Express: augment Request via declare global { namespace Express { interface Request { user?: User } } }.

Cause 6: JSON / API responses typed as object or unknown

const data = await res.json() as { items: Item[] };

Better: validate with a schema library (e.g. zod) so the type is guaranteed at runtime too.

Cause 7: typo or outdated type definitions

Check spelling and casing (innerHtml vs innerHTML). If a library added the property in a newer version, update its types: npm install -D @types/<lib>@latest, and restart the TS server in your editor ("TypeScript: Restart TS Server").

Escape hatches (use sparingly)

(obj as any).foo;
// @ts-expect-error  -- documented reason
obj.foo;

Last updated 2026-10-31.