> Every UQL docs page, as Markdown: https://uql-orm.dev/llms.txt
> The same docs over MCP: https://uql-orm.dev/mcp
> Before writing UQL code, read the skill: https://uql-orm.dev/.well-known/agent-skills/uql-orm/SKILL.md

# Inheritance

> Declare shared fields once in a base class and inherit them in every entity.

Source: https://uql-orm.dev/entities/inheritance

An entity inherits what the class it extends declares, abstract or concrete, so shared columns are declared once:

```ts
import { Entity, Id, Field } from 'uql-orm';
import { v7 as uuidv7 } from 'uuid';

export abstract class BaseEntity {
  @Id({ type: String, onInsert: uuidv7 }) id?: string;
  @Field({ type: 'timestamptz', onInsert: () => new Date() })
  createdAt?: Date | null;
  @Field({ type: 'timestamptz', onUpdate: () => new Date() })
  updatedAt?: Date | null;
}

@Entity({ name: 'company' })
export class Company extends BaseEntity {
  @Field({ type: String, length: 150 }) name?: string | null;
}
```

A child inherits the base’s fields, relations, hooks, filters, indexes, checks and triggers. They are typed and queried like its own, and a change to the base reaches every child. The base’s entries come first, and a child entry with the same name replaces the base’s. The base needs no `@Entity()` unless it is a table too. A child may add nothing of its own, which gives a second table of the same shape:

```ts
@Index((entity) => [entity.tenantId])
@Trigger({ on: 'afterInsert', name: 'audit', run: logInsert })
abstract class Tenanted {
  @Field({ type: String }) tenantId?: string | null;
}

@Entity()
export class Invoice extends Tenanted {} // gets its own tenant index and audit trigger
```

Each table derives its own index and trigger names. An index you name yourself keeps that name in every child, and PostgreSQL and SQLite need index names unique across tables, so leave a shared index unnamed.

## Replacing an inherited key

A child that declares its own `@Id` replaces the parent’s key entirely (every column of it, if it was [composite](https://uql-orm.dev/entities/basic.md#composite-primary-keys)) and keeps every other inherited field. Name the new key with the `idKey` brand, or TypeScript still reads the inherited `id` as the key:

```ts
import { Entity, Id, Field, idKey } from 'uql-orm';
import { v7 as uuidv7 } from 'uuid';

@Entity()
export class TaxCategory extends BaseEntity {
  [idKey]?: 'pk';

  @Id({ type: String, onInsert: uuidv7 }) pk?: string;
  @Field({ type: String }) name?: string | null;
}
```

## Naming a base you cannot extend

A class created at runtime, or one whose base is chosen from data, has no `extends` clause. Pass the base to `defineEntity` as `extends` instead:

```ts
import { defineEntity, defineField } from 'uql-orm';

class Audited {
  createdBy?: string;
}
defineField(Audited, 'createdBy', { type: String });

// a class built at runtime, with no extends clause
const Faq = {
  faq: class {
    id!: number;
    question?: string | null;
    createdBy?: string;
  },
}['faq'];

defineEntity(Faq, {
  extends: Audited,
  fields: { id: { type: 'bigint', isId: true }, question: { type: 'text' } },
});
```

It merges like a class `extends`: the base’s own ancestors come too, the child’s members win, and a child’s own key replaces the base’s. When a class both extends a base in code and names one in `extends`, the nearer one wins.

The base’s properties are type-checked against the entity’s, so a base declaring `createdBy?: number` here is a compile error. A class that declares no properties, such as a runtime class with an index signature, accepts any base.
