# Refused Bequest

> A subclass inherits methods or data it does not want, overriding them to throw or do nothing, so the hierarchy lies about what it can do.

- kind: smell
- category: oo-abusers
- canonical: https://aipatterns.dev/refactoring/smells/refused-bequest
- inspired-by: https://refactoring.guru/smells/refused-bequest

## Signs

- A subclass overrides inherited methods to throw `NotSupportedError` or return without doing anything.
- Callers check `instanceof` before calling a base-class method, because some subclasses can't honor it.
- The subclass ignores most inherited fields; they sit unused on every instance.
- The inheritance was chosen to reuse a few helper methods, not because the subclass "is a" kind of the parent.

## Why it happens

Inheritance is the quickest way to reuse code: extend a class and its methods are yours. When the new class needs only part of the parent, the rest gets stubbed out. The hierarchy then claims a substitutability that does not hold, which breaks callers that trust the base type.

## Example

A read-only archive storage that extends a general-purpose file store.

```ts title="storage.ts"
class FileStore {
  constructor(protected bucket: string) {}
  async read(key: string): Promise<Buffer> { /* ... */ return Buffer.alloc(0); }
  async list(prefix: string): Promise<string[]> { /* ... */ return []; }
  async write(key: string, data: Buffer): Promise<void> { /* ... */ }
  async delete(key: string): Promise<void> { /* ... */ }
}

class ArchiveStore extends FileStore {
  async write(): Promise<void> {
    throw new Error("archive is read-only");
  }
  async delete(): Promise<void> {
    throw new Error("archive is read-only");
  }
}

async function cleanup(store: FileStore) {
  for (const key of await store.list("tmp/")) await store.delete(key); // explodes for ArchiveStore
}
```

Splitting the capability into a narrower interface and using delegation makes the type honest.

```ts title="storage.ts"
interface ReadableStore {
  read(key: string): Promise<Buffer>;
  list(prefix: string): Promise<string[]>;
}

interface WritableStore extends ReadableStore {
  write(key: string, data: Buffer): Promise<void>;
  delete(key: string): Promise<void>;
}

class FileStore implements WritableStore { /* unchanged */ }

class ArchiveStore implements ReadableStore {
  constructor(private readonly inner: FileStore) {}
  read(key: string) { return this.inner.read(key); }
  list(prefix: string) { return this.inner.list(prefix); }
}

async function cleanup(store: WritableStore) { /* ArchiveStore no longer type-checks here */ }
```

## Treatment

- If the subclass is not truly a kind of the parent and only wants some of its behavior, use [Replace Inheritance with Delegation](https://aipatterns.dev/refactoring/techniques/replace-inheritance-with-delegation.md). Hold the former parent as a field and forward only what you need.
- If inheritance is appropriate but the parent has members only some children use, create a sibling class and use [Push Down Method](https://aipatterns.dev/refactoring/techniques/push-down-method.md) and [Push Down Field](https://aipatterns.dev/refactoring/techniques/push-down-field.md) to move those members into the children that want them.
- If two classes share a genuine core and differ elsewhere, use [Extract Superclass](https://aipatterns.dev/refactoring/techniques/extract-superclass.md) for the shared core and keep the rest separate.
- Split wide interfaces by capability (read, write, admin) so each implementation declares only what it supports.

## Payoff

- Any object passed as the base type actually supports the base type's operations.
- `instanceof` checks and runtime "not supported" errors disappear.
- The compiler rejects misuse that previously failed in production.
- The hierarchy documents real relationships instead of code-sharing shortcuts.

## When to ignore

- The refused method is rarely called and the parent is a framework class you cannot change; a documented no-op may be the least bad option.
- The subclass refuses an *implementation* but still honors the *interface*, for example by overriding a method with a different but valid behavior. That is ordinary specialization.
- The standard library does it deliberately, such as read-only collection views that throw on mutation, and callers are used to it. Match the convention of your platform.

## Agent checklist

- [ ] You listed every inherited member the subclass overrides to throw, ignore or leave unused.
- [ ] You checked callers that accept the base type and confirmed which ones would break with the subclass.
- [ ] After the change, no method in the hierarchy exists only to throw "not supported".
- [ ] Callers that need the narrower capability accept the narrower type.
- [ ] Tests pass, and a type-level or unit test shows the invalid call is now rejected.
