---
title: Field types
description: Complete reference for the env builder namespace — all ten field types and their chain modifiers.
url: https://pr-1-ee19382a0710.thally.app/envlock/field-types
---

# Field types

Complete reference for the env builder namespace — all ten field types and their chain modifiers.

The `env` builder namespace provides one factory for every supported field type. Each factory returns a required, non-secret field that you refine with chain modifiers such as `.optional()`, `.default()`, and `.secret()`.

## Field types

### `env.string()`

Verbatim pass-through. The raw value is returned as-is with no trimming or transformation.

```js
import { env } from "@envlock/core";

const field = env.string();
// "hello world" -> "hello world"
```

### `env.number()`

Parses the value with `Number()` after trimming whitespace. The result must be finite. Blank strings are rejected rather than silently converting to `0`.

```js
const field = env.number();
// "3.14"  -> 3.14
// ""      -> error: "expected a number, received an empty string"
// "hello" -> error: "expected a number"
```

**Constraints:** `"finite number"`

### `env.integer()`

Parses like `env.number()` but additionally requires that the result passes `Number.isSafeInteger`.

```js
const field = env.integer();
// "42"   -> 42
// "3.14" -> error: "expected a whole number"
```

**Constraints:** `"whole number"`

### `env.boolean()`

Accepts the following spellings (case-insensitive):

| True | False |
|------|-------|
| `true` | `false` |
| `1` | `0` |
| `yes` | `no` |
| `on` | `off` |

```js
const field = env.boolean();
// "YES"   -> true
// "0"     -> false
// "maybe" -> error: "expected one of true/false, 1/0, yes/no, on/off"
```

**Constraints:** `"true/false, 1/0, yes/no, on/off"`

### `env.port()`

An integer in the range 1 through 65535.

```js
const field = env.port();
// "8080"  -> 8080
// "0"     -> error: "expected a port number between 1 and 65535"
// "70000" -> error: "expected a port number between 1 and 65535"
```

**Constraints:** `"integer 1-65535"`

### `env.url(options?)`

Validates the value with `new URL()`. You can restrict accepted protocols by passing a `protocols` array. Each protocol string must include the trailing colon.

```js
const field = env.url({ protocols: ["https:", "postgres:"] });
// "https://example.com"  -> "https://example.com"
// "not-a-url"            -> error: "expected an absolute URL such as https://example.com"
// "http://example.com"   -> error: "expected a URL using https: or postgres:"
```

The `UrlOptions` interface:

```ts
interface UrlOptions {
  readonly protocols?: readonly string[];
}
```

**Constraints:** `"absolute URL"` or `"absolute URL with protocol X or Y"` when protocols are specified.

### `env.enum(values)`

Requires an exact match against one of the provided string literals. The `values` argument is a readonly tuple of at least one string.

```js
const field = env.enum(["debug", "info", "warn", "error"]);
// "info"    -> "info"
// "verbose" -> error: "expected one of: debug, info, warn, error"
```

**Constraints:** `"one of: <values>"`

### `env.json()`

Parses the value with `JSON.parse()`. Accepts a generic type parameter for the parsed result.

```js
const field = env.json();
// '{"a":1}' -> { a: 1 }
// 'not json' -> error: "expected valid JSON (<parse error detail>)"
```

In TypeScript, you can narrow the parsed type:

```ts
const field = env.json<{ enabled: boolean }>();
```

**Constraints:** `"JSON document"`

### `env.duration()`

Parses a number with an optional unit suffix. Accepted units are `ms`, `s`, `m`, `h`, and `d`. A bare number without a suffix is treated as milliseconds. The result is stored as milliseconds, rounded to the nearest integer.

| Unit | Multiplier |
|------|-----------|
| `ms` | 1 |
| `s` | 1,000 |
| `m` | 60,000 |
| `h` | 3,600,000 |
| `d` | 86,400,000 |

```js
const field = env.duration();
// "30s"  -> 30000
// "5m"   -> 300000
// "250"  -> 250  (bare number = milliseconds)
// "fast" -> error: "expected a duration like 30s, 5m, 2h, 1d or 250ms"
```

**Constraints:** `"duration like 30s, 5m, 2h, stored as milliseconds"`

### `env.list(options?)`

Splits the value by a separator (comma by default), trims each item, and drops empty items.

```js
const field = env.list();
// "a, b, c" -> ["a", "b", "c"]
// "a;;b"    -> ["a;;b"]  (single item, comma is the default separator)

const pipeList = env.list({ separator: "|" });
// "x | y | z" -> ["x", "y", "z"]
```

The `ListOptions` interface:

```ts
interface ListOptions {
  readonly separator?: string;  // default: ","
}
```

**Constraints:** `"comma-separated list"` or `"<separator>-separated list"` when a custom separator is used.

## Field modifiers

Every field supports the following chain methods. Fields are immutable (frozen); each modifier returns a new field instance rather than mutating the original.

### `.optional()`

Marks the field as not required. When the variable is absent from the environment, no error is raised and the value is `undefined`.

```js
const field = env.string().optional();
```

### `.default(value)`

Provides a fallback value used when the variable is absent. Setting a default clears the optional flag -- the field is always guaranteed to have a value.

```js
const field = env.port().default(3000);
```

### `.secret()`

Marks the field as containing sensitive data. When a secret field fails validation, the received value is masked as `"••••••"` in error output instead of being displayed in clear text.

```js
const field = env.string().secret();
```

### `.describe(text)`

Attaches a human-readable description. Used by `renderExample()` and `describeSchema()` to annotate generated output.

```js
const field = env.port().describe("HTTP listen port");
```

### `.example(text)`

Provides an example value for generated `.env.example` files.

```js
const field = env.url().example("https://api.example.com/v1");
```

## Field interface

The full `Field` interface exposed by `@envlock/core`:

```ts
interface Field<T = unknown, Required extends boolean = boolean> {
  readonly kind: FieldKind;
  readonly isOptional: boolean;
  readonly hasDefault: boolean;
  readonly defaultValue?: T;
  readonly isSecret: boolean;
  readonly description?: string;
  readonly exampleValue?: string;
  readonly constraints?: string;
  readonly parse: (raw: string) => ParseOutcome<T>;

  optional(): Field<T, false>;
  default(value: T): Field<T, true>;
  secret(): Field<T, Required>;
  describe(text: string): Field<T, Required>;
  example(text: string): Field<T, Required>;
}
```

`FieldKind` is a union of all ten type names:

``"string" | "number" | "integer" | "boolean" | "port" | "url" | "enum" | "json" | "duration" | "list"``

`ParseOutcome` is the result of a field's parse function:

```ts
type ParseOutcome<T> =
  | { readonly ok: true; readonly value: T }
  | { readonly ok: false; readonly message: string };
```

## Parse error messages

For reference, here are the exact error messages each field type produces on invalid input:

| Field type | Condition | Error message |
|-----------|-----------|---------------|
| `number` | Blank string | `"expected a number, received an empty string"` |
| `number` | NaN or Infinity | `"expected a number"` |
| `integer` | Non-integer number | `"expected a whole number"` |
| `boolean` | Unrecognized value | `"expected one of true/false, 1/0, yes/no, on/off"` |
| `port` | Out of range or non-integer | `"expected a port number between 1 and 65535"` |
| `url` | Unparseable URL | `"expected an absolute URL such as https://example.com"` |
| `url` | Wrong protocol | `"expected a URL using X or Y"` |
| `enum` | Not a member | `"expected one of: <values>"` |
| `json` | Invalid JSON | `"expected valid JSON (<detail>)"` |
| `duration` | Unrecognized format | `"expected a duration like 30s, 5m, 2h, 1d or 250ms"` |