How to fix Property 'X' is unreachable because index signature covers all properties in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 11, 2025· 111 views

This TypeScript error occurs when you define both specific properties and an index signature on the same type, making the specific properties unreachable. The index signature acts as a catch-all that overshadows any explicitly defined properties, preventing TypeScript from accessing them safely.

Quick fix

Fixes TS2411

// WRONG - causes TS2411 error
interface User {
  name: string;
  age: number;
  [key: string]: any;  // Index signature makes 'name' and 'age' unreachable
}

// CORRECT - separate types
interface UserBase {
  name: string;
  age: number;
}

type User = UserBase & {
  [key: string]: any;
};

// Usage
const user: User = {
  name: "Alice",
  age: 30,
  extraField: "value",  // Allowed by index signature
};

console.log(user.name);  // Type-safe: string
console.log(user.age);   // Type-safe: number
  1. Separate known and unknown properties into different types
  2. Use Record type for index signatures with specific properties
  3. Use optional properties instead of index signatures
  4. Use type guards to check property existence
typescript — error
// WRONG - causes TS2411 error
interface User {
name: string;
age: number;
[key: string]: any; // Index signature makes 'name' and 'age' unreachable
}
// CORRECT - separate types
interface UserBase {
name: string;
age: number;
}
type User = UserBase & {
[key: string]: any;
};
// Usage
const user: User = {
name: "Alice",
age: 30,
extraField: "value", // Allowed by index signature
};
console.log(user.name); // Type-safe: string
console.log(user.age); // Type-safe: number
Property 'X' is unreachable because index signature covers all properties

What this error means

The "Property is unreachable because index signature covers all properties" error (TS2411) happens when you combine explicit property definitions with an index signature in the same type. An index signature like `[key: string]: T` means "this object can have any string key with value type T." When you also define specific properties like `name: string`, those properties become unreachable because the index signature already covers all possible keys, including 'name'. This creates a type safety issue: TypeScript cannot guarantee that accessing `obj.name` will return a string, because the index signature allows any string key to have any value of type T. Even if T is string, the index signature doesn't enforce that 'name' specifically returns a string - it could theoretically be overridden. The error prevents you from creating types that are both open (allow arbitrary properties) and closed (have specific typed properties) in the same declaration. You need to choose one approach or restructure your types to separate concerns.

Typical symptoms

  • TypeScript compilation fails with TS2411 error
  • Error message shows 'Property 'X' is unreachable because index signature covers all properties'
  • IDE shows red underlines on interface or type definitions
  • Occurs when combining explicit properties with index signatures
  • Error appears when defining interfaces for objects that should have both known and unknown properties
  • Type checking fails even though the code might work at runtime

Common causes

  1. Defining an index signature alongside explicit properties in the same interface/type
  2. Using `[key: string]: T` or `[key: number]: T` with specific property definitions
  3. Trying to create a type that's both open (accepts arbitrary properties) and closed (has specific typed properties)
  4. Inheriting from a type with an index signature and adding specific properties
  5. Merging interfaces where one has an index signature and another adds specific properties
  6. Using mapped types incorrectly with index signatures

How to fix "Property 'X' is unreachable because index signature covers all properties"

1Separate known and unknown properties into different types

Instead of mixing index signatures with explicit properties, create separate types and combine them:

typescript
// WRONG - causes TS2411 error
interface User {
  name: string;
  age: number;
  [key: string]: any;  // Index signature makes 'name' and 'age' unreachable
}

// CORRECT - separate types
interface UserBase {
  name: string;
  age: number;
}

type User = UserBase & {
  [key: string]: any;
};

// Usage
const user: User = {
  name: "Alice",
  age: 30,
  extraField: "value",  // Allowed by index signature
};

console.log(user.name);  // Type-safe: string
console.log(user.age);   // Type-safe: number

The intersection type (&) combines the specific properties with the index signature while maintaining type safety.

2Use Record type for index signatures with specific properties

If you need a type with required properties plus arbitrary additional ones, use Record with intersection:

typescript
// Using Record type
type User = {
  name: string;
  age: number;
} & Record<string, any>;

// Alternative with interface extension
interface UserBase {
  name: string;
  age: number;
}

interface User extends UserBase {
  [key: string]: any;
}
// Note: This still causes TS2411! Use type intersection instead.

// Better: Use type alias with intersection
type User = UserBase & Record<string, any>;

// For specific value types
type Config = {
  apiUrl: string;
  timeout: number;
} & Record<string, string | number | boolean>;

const config: Config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  debug: true,      // Additional boolean property
  retries: 3,       // Additional number property
};

The Record<string, any> creates an index signature that allows any additional string properties.

3Use optional properties instead of index signatures

If you know all possible property names, use optional properties instead of an index signature:

typescript
// Instead of index signature for optional metadata
interface Product {
  id: string;
  name: string;
  price: number;
  metadata?: {
    [key: string]: any;  // Index signature in nested object is fine
  };
}

// Or use a specific type for additional data
interface Product {
  id: string;
  name: string;
  price: number;
  tags: string[];
  attributes: Record<string, string | number>;
}

// For React component props
interface ButtonProps {
  onClick: () => void;
  children: React.ReactNode;
  className?: string;
  disabled?: boolean;
  // Instead of [key: string]: any, use specific optional props
  'data-testid'?: string;
  'aria-label'?: string;
}

This approach is more type-safe and makes your API clearer.

4Use type guards to check property existence

When working with objects that might have arbitrary properties, use type guards:

typescript
type User = {
  name: string;
  age: number;
} & Record<string, unknown>;

function isUser(obj: any): obj is User {
  return (
    typeof obj === 'object' &&
    obj !== null &&
    typeof obj.name === 'string' &&
    typeof obj.age === 'number'
  );
}

// Safe access with type guards
function getUserName(user: User): string {
  return user.name;  // Type-safe because we know User has name
}

// Accessing arbitrary properties
function getOptionalField(user: User, field: string): unknown {
  return user[field];  // Returns unknown, need to check type
}

// Type-safe access with narrowing
if (typeof user.customField === 'string') {
  console.log(user.customField.toUpperCase());
}

Type guards provide runtime safety while maintaining TypeScript's type checking.

5Consider using Map or Set for dynamic properties

If you need truly dynamic key-value pairs, consider using Map instead of objects with index signatures:

typescript
// Using Map for dynamic properties
interface User {
  name: string;
  age: number;
  metadata: Map<string, any>;  // Instead of [key: string]: any
}

const user: User = {
  name: "Bob",
  age: 25,
  metadata: new Map([
    ['role', 'admin'],
    ['lastLogin', new Date()],
    ['preferences', { theme: 'dark' }]
  ])
};

// Type-safe access to known properties
console.log(user.name);

// Access dynamic properties via Map
const role = user.metadata.get('role');
if (typeof role === 'string') {
  console.log(role.toUpperCase());
}

// Alternative: Use a separate object for dynamic properties
interface User {
  name: string;
  age: number;
  extra: Record<string, any>;
}

const user2: User = {
  name: "Charlie",
  age: 35,
  extra: {
    department: "Engineering",
    salary: 80000
  }
};

Maps provide better type safety and performance for dynamic key-value stores.

6Refactor to use discriminated unions or type predicates

For complex scenarios, use discriminated unions or custom type predicates:

typescript
// Discriminated union for different object shapes
type ApiResponse =
  | { type: 'user'; data: UserData; metadata: Record<string, any> }
  | { type: 'product'; data: ProductData; tags: string[] }
  | { type: 'error'; message: string; code: number };

function handleResponse(response: ApiResponse) {
  switch (response.type) {
    case 'user':
      // response.data is UserData
      // response.metadata is Record<string, any>
      break;
    case 'product':
      // response.data is ProductData
      // response.tags is string[]
      break;
    case 'error':
      // response.message is string
      // response.code is number
      break;
  }
}

// Custom type predicate for validation
function hasProperty<T extends object, K extends string>(
  obj: T,
  key: K
): obj is T & Record<K, any> {
  return key in obj;
}

const obj = { name: 'Alice', age: 30 } as Record<string, any>;
if (hasProperty(obj, 'email')) {
  // obj.email is now typed as any
  console.log(obj.email);
}

These patterns provide better type safety than index signatures.

Advanced notes

### Understanding Index Signatures

Index signatures in TypeScript define the type for properties when the property name isn't known at compile time. They come in two forms:

typescript
// String index signature
interface StringIndex {
  [key: string]: number;
}

// Number index signature
interface NumberIndex {
  [key: number]: string;
}

Key constraints:
1. All explicit property types must be assignable to the index signature type
2. You can only have one string index signature and one number index signature per type
3. If both string and number index signatures exist, number index return type must be subtype of string index return type

### Why Properties Become Unreachable

When TypeScript sees [key: string]: T, it means "for any string key, the value is T." If you also define name: string, accessing obj.name should return string. But the index signature says it returns T. If T is any or unknown, there's no guarantee obj.name is string.

TypeScript's type system is structural, not nominal. The property 'name' isn't special - it's just another string key. The index signature already covers it, making the explicit declaration redundant and potentially contradictory.

### Workarounds and Alternatives

1. Intersection Types (Recommended):

typescript
type MyType = {
  knownProp: string;
  anotherProp: number;
} & Record<string, any>;

2. Type Assertions (Use Sparingly):

typescript
interface UnsafeType {
  name: string;
  [key: string]: any;
} as any;  // Not recommended

3. Separate Interfaces:

typescript
interface KnownProps {
  name: string;
  age: number;
}

interface DynamicProps {
  [key: string]: any;
}

type Combined = KnownProps & DynamicProps;

4. Generic Constraints:

typescript
function process<T extends Record<string, any>>(obj: T & { id: string }) {
  // obj has id: string plus any other properties
}

### Real-World Use Cases

API Responses:

typescript
type ApiResponse<T> = {
  data: T;
  status: number;
} & Record<string, any>;  // For headers, metadata, etc.

Configuration Objects:

typescript
type AppConfig = {
  apiUrl: string;
  timeout: number;
} & Record<string, string | number | boolean>;

Component Props (React/Vue):

typescript
// Instead of [key: string]: any for HTML attributes
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
  variant?: 'primary' | 'secondary';
  size?: 'sm' | 'md' | 'lg';
};

### TypeScript Configuration

If you're working with external libraries that use index signatures, ensure your tsconfig.json has appropriate settings:

json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    // These help catch index signature issues early
    "noUncheckedIndexedAccess": true,  // Adds undefined to index accesses
    "exactOptionalPropertyTypes": true  // Makes optional properties more strict
  }
}

The noUncheckedIndexedAccess flag is particularly useful as it makes obj[key] return T | undefined instead of just T, reminding you that properties accessed via index might not exist.

### Migration Strategy

If you have existing code with mixed index signatures and explicit properties:

1. Identify all occurrences: Search for \[key: string\]: or \[key: number\]:
2. Separate concerns: Move explicit properties to base interfaces
3. Use intersection types: Combine with Record<string, any> or similar
4. Add type guards: For runtime safety with dynamic properties
5. Test thoroughly: Index signature changes can affect type inference broadly

Remember: Index signatures are powerful but should be used judiciously. They sacrifice type safety for flexibility. Always prefer explicit property definitions when possible.

Frequently asked questions

What does "TS2411" mean?
The "Property is unreachable because index signature covers all properties" error (TS2411) happens when you combine explicit property definitions with an index signature in the same type. " When you also define specific properties like name: string, those properties become unreachable because the index signature already covers all possible keys, including 'name'. name will return a string, because the index signature allows any string key to have any value of type T.
How do I fix "TS2411"?
This TypeScript error occurs when you define both specific properties and an index signature on the same type, making the specific properties unreachable. The index signature acts as a catch-all that overshadows any explicitly defined properties, preventing TypeScript from accessing them safely.
What causes "TS2411"?
Defining an index signature alongside explicit properties in the same interface/type Using [key: string]: T or [key: number]: T with specific property definitions Trying to create a type that's both open (accepts arbitrary properties) and closed (has specific typed properties)
Is the "TS2411" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading