[API reference](https://evolu.dev/docs/api-reference) › [@evolu/common](https://evolu.dev/docs/api-reference/common) › [Type](https://evolu.dev/docs/api-reference/common/Type) › Typed

Defined in: [packages/common/src/Type.ts:3491](https://github.com/evoluhq/evolu/blob/e7144e2bbe9069362b62dec1b64a8aa922b8f1b0/packages/common/src/Type.ts#L3491)

Base interface for objects with a discriminant `type` property.

This enables
[discriminated unions](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions)
(also known as tagged unions) — a pattern where TypeScript uses a literal
`type` field to narrow union types automatically.

## Why Discriminated Unions?

Discriminated unions model states that are **mutually exclusive**. Instead of
optional fields and boolean flags that can combine into invalid
configurations, each variant is a distinct type. This makes illegal states
unrepresentable — invalid combinations cannot exist, so bugs cannot create
them.

Benefits:

- **Self-documenting** — Union cases immediately show all possible states
- **Compile-time safety** — TypeScript enforces handling all cases
- **Refactoring-friendly** — Adding a new state breaks code that doesn't handle
  it

### Example

```ts
// Bad: optional fields allow invalid states (no contact info at all)
interface Contact {
  readonly email?: Email;
  readonly phone?: Phone;
}

// Good: discriminated union makes "at least one" explicit
interface EmailOnly extends Typed<"EmailOnly"> {
  readonly email: Email;
}
interface PhoneOnly extends Typed<"PhoneOnly"> {
  readonly phone: Phone;
}
interface EmailAndPhone extends Typed<"EmailAndPhone"> {
  readonly email: Email;
  readonly phone: Phone;
}

type ContactInfo = EmailOnly | PhoneOnly | EmailAndPhone;
```

```ts
interface Pending extends Typed<"Pending"> {
  readonly createdAt: DateIso;
}
interface Shipped extends Typed<"Shipped"> {
  readonly trackingNumber: TrackingNumber;
}
interface Delivered extends Typed<"Delivered"> {
  readonly deliveredAt: DateIso;
}
interface Cancelled extends Typed<"Cancelled"> {
  readonly reason: CancellationReason;
}

type OrderState = Pending | Shipped | Delivered | Cancelled;

// TypeScript enforces exhaustiveness via return type
const getStatusMessage = (state: OrderState): string => {
  switch (state.type) {
    case "Pending":
      return "Order placed";
    case "Shipped":
      return `Shipped: ${state.trackingNumber}`;
    case "Delivered":
      return `Delivered on ${state.deliveredAt.toLocaleDateString()}`;
    case "Cancelled":
      return `Cancelled: ${state.reason}`;
  }
};

// For void functions, use exhaustiveCheck to ensure all cases are handled
const logState = (state: OrderState): void => {
  switch (state.type) {
    case "Pending":
      console.log("Order placed");
      break;
    case "Shipped":
      console.log(`Shipped: ${state.trackingNumber}`);
      break;
    case "Delivered":
      console.log(`Delivered on ${state.deliveredAt.toLocaleDateString()}`);
      break;
    case "Cancelled":
      console.log(`Cancelled: ${state.reason}`);
      break;
    default:
      exhaustiveCheck(state);
  }
};
```

## Why `type` (and not e.g. `_tag`)?

Underscore-prefixing is meant to avoid clashing with domain properties, but
proper discriminated union design means the discriminant IS the domain
concept — there's no clash to avoid. The `type` prop name also aligns with
[Type](https://evolu.dev/docs/api-reference/common/Type/interfaces/Type)'s name. If an entity has a meaningful "type" (like product
category), model it as the discriminant itself:

```ts
interface Electronics extends Typed<"Electronics"> {
  voltage: Voltage;
}
interface Clothing extends Typed<"Clothing"> {
  size: Size;
}
type Product = Electronics | Clothing;
```

## See

- [exhaustiveCheck](https://evolu.dev/docs/api-reference/common/Function/functions/exhaustiveCheck) to ensure all cases are handled in void functions.
- [typed](https://evolu.dev/docs/api-reference/common/Type/functions/typed) for runtime-validated typed objects.

## Extended by

- [`DecryptWithXChaCha20Poly1305Error`](https://evolu.dev/docs/api-reference/common/Crypto/interfaces/DecryptWithXChaCha20Poly1305Error)
- [`UnknownError`](https://evolu.dev/docs/api-reference/common/Error/interfaces/UnknownError)
- [`Some`](https://evolu.dev/docs/api-reference/common/Option/interfaces/Some)
- [`None`](https://evolu.dev/docs/api-reference/common/Option/interfaces/None)
- [`Done`](https://evolu.dev/docs/api-reference/common/Result/interfaces/Done)
- [`SqlIdentifier`](https://evolu.dev/docs/api-reference/common/Sqlite/interfaces/SqlIdentifier)
- [`RawSql`](https://evolu.dev/docs/api-reference/common/Sqlite/interfaces/RawSql)
- [`RunStateRunning`](https://evolu.dev/docs/api-reference/common/Task/interfaces/RunStateRunning)
- [`RunStateDisposing`](https://evolu.dev/docs/api-reference/common/Task/interfaces/RunStateDisposing)
- [`RunStateSettled`](https://evolu.dev/docs/api-reference/common/Task/interfaces/RunStateSettled)
- [`RetryError`](https://evolu.dev/docs/api-reference/common/Task/interfaces/RetryError)
- [`WebSocketSendError`](https://evolu.dev/docs/api-reference/common/WebSocket/interfaces/WebSocketSendError)
- [`WebSocketConnectError`](https://evolu.dev/docs/api-reference/common/WebSocket/interfaces/WebSocketConnectError)
- [`WebSocketConnectionError`](https://evolu.dev/docs/api-reference/common/WebSocket/interfaces/WebSocketConnectionError)
- [`WebSocketConnectionCloseError`](https://evolu.dev/docs/api-reference/common/WebSocket/interfaces/WebSocketConnectionCloseError)
- [`AppOwner`](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/AppOwner)
- [`ShardOwner`](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/ShardOwner)
- [`SharedOwner`](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/SharedOwner)
- [`SharedReadonlyOwner`](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/SharedReadonlyOwner)
- [`OwnerWebSocketTransport`](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/OwnerWebSocketTransport)
- [`ProtocolHeader`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolHeader)
- [`ProtocolVersionError`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolVersionError)
- [`ProtocolInvalidDataError`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolInvalidDataError)
- [`ProtocolWriteKeyError`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolWriteKeyError)
- [`ProtocolWriteError`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolWriteError)
- [`ProtocolQuotaError`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolQuotaError)
- [`ProtocolSyncError`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolSyncError)
- [`ProtocolTimestampMismatchError`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolTimestampMismatchError)
- [`ApplyProtocolMessageAsClientResponse`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsClientResponse)
- [`ApplyProtocolMessageAsClientNoResponse`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsClientNoResponse)
- [`ApplyProtocolMessageAsClientBroadcast`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsClientBroadcast)
- [`ApplyProtocolMessageAsRelayResult`](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsRelayResult)
- [`StorageQuotaError`](https://evolu.dev/docs/api-reference/common/local-first/Storage/interfaces/StorageQuotaError)
- [`TimestampDriftError`](https://evolu.dev/docs/api-reference/common/local-first/Timestamp/interfaces/TimestampDriftError)
- [`TimestampCounterOverflowError`](https://evolu.dev/docs/api-reference/common/local-first/Timestamp/interfaces/TimestampCounterOverflowError)
- [`TimestampTimeOutOfRangeError`](https://evolu.dev/docs/api-reference/common/local-first/Timestamp/interfaces/TimestampTimeOutOfRangeError)

## Properties

<a id="type"></a>

### type

```ts
readonly type: T;
```

Defined in: [packages/common/src/Type.ts:3492](https://github.com/evoluhq/evolu/blob/e7144e2bbe9069362b62dec1b64a8aa922b8f1b0/packages/common/src/Type.ts#L3492)