[API reference](https://evolu.dev/docs/api-reference) › [@evolu/common](https://evolu.dev/docs/api-reference/common) › [local‑first/Timestamp](https://evolu.dev/docs/api-reference/common/local-first/Timestamp) › TimestampDriftError

Defined in: [packages/common/src/local-first/Timestamp.ts:50](https://github.com/evoluhq/evolu/blob/e7144e2bbe9069362b62dec1b64a8aa922b8f1b0/packages/common/src/local-first/Timestamp.ts#L50)

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.

## Extends

- [`Typed`](https://evolu.dev/docs/api-reference/common/Type/interfaces/Typed)\<`"TimestampDriftError"`\>

## Properties

<a id="next"></a>

### next

```ts
readonly next: number & Brand<"Int"> & Brand<"NonNegative"> & Brand<"LessThan281474976710655"> & Brand<"Millis">;
```

Defined in: [packages/common/src/local-first/Timestamp.ts:51](https://github.com/evoluhq/evolu/blob/e7144e2bbe9069362b62dec1b64a8aa922b8f1b0/packages/common/src/local-first/Timestamp.ts#L51)

---

<a id="now"></a>

### now

```ts
readonly now: number & Brand<"Int"> & Brand<"NonNegative"> & Brand<"LessThan281474976710655"> & Brand<"Millis">;
```

Defined in: [packages/common/src/local-first/Timestamp.ts:52](https://github.com/evoluhq/evolu/blob/e7144e2bbe9069362b62dec1b64a8aa922b8f1b0/packages/common/src/local-first/Timestamp.ts#L52)

---

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

### type

```ts
readonly type: "TimestampDriftError";
```

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

#### Inherited from

[`Typed`](https://evolu.dev/docs/api-reference/common/Type/interfaces/Typed).[`type`](https://evolu.dev/docs/api-reference/common/Type/interfaces/Typed#type)