---
url: /typed-schema/guide/declaring-a-document.md
---
# Declaring a document

`document()` declares a document type. Its fields are schemas made with the other creators, and `Infer` turns the result into a TypeScript type.

```ts
import * as s from '@sanity-labs/typed-schema'

export const User = s.document({
  _type: s.literal('user'),
  name: s.string(),
  idOrHandle: s.union([s.string(), s.number()]),
  isAdmin: s.optional(s.boolean()),
  address: s.object({
    city: s.optional(s.string()),
  }),
})

export type User = s.Infer<typeof User>

const result = s.parse(User, {
  _type: 'user',
  name: 'Grace Hopper',
  idOrHandle: 'grace',
  isAdmin: true,
  address: {city: 'Arlington'},
})

if (result.ok) {
  const user = result.value

  user.address

  // a typo in a field name is a type error
  user.adress
}
```

Schema constants are PascalCase (`User`, `Address`), the same as the type inferred from them. TypeScript lets a `const` and a `type` share a name, so `User` is the schema in a value position and the parsed document type in a type position, and one import gives you both. Creator and transform functions (`document()`, `reference()`, `extend()`) stay camelCase, and `_type` literals keep Sanity's camelCase type names (`'user'`).

A document schema needs a `_type` literal, and gets the system fields `_id`, `_rev`, `_createdAt` and `_updatedAt` (see [`document()`](/api/schema-types#document)). Use `object()` for nested objects; give it a `_type` literal when the object is a member of a union.

See the [schema types reference](/api/schema-types) for every creator.
