# Selectors (/docs/selectors)



Overview [#overview]

A **selector** is a string that points to one or more nodes inside an object or array.
Selectors are accepted by filters and redactions to identify which parts of the snapshot to
transform.

```ts
// remove a nested field
new ExcludeFilter(".user.password");

// redact every token in an array
new ReplacedRedaction(".tokens[]", "[TOKEN]");

// sort a deeply-nested array
new SortedRedaction(".**.tags");
```

Selectors compose: segments are simply concatenated, so `.user.address[0].street` is read as
&#x2A;key `user` → key `address` → index `0` → key `street`*.

Syntax [#syntax]

| Selector      | Matches                                  |
| ------------- | ---------------------------------------- |
| `.key`        | Property `key` on an object              |
| `["key"]`     | Same, bracket notation                   |
| `.a.b.c`      | Nested path                              |
| `[n]`         | Array item at index `n`                  |
| `[-1]`        | Last array item                          |
| `[]`          | All array items                          |
| `[start:end]` | Array slice (negative indices supported) |
| `.*`          | All keys of an object (wildcard)         |
| `.**`         | All descendants (recursive)              |
| `.**.key`     | All `key` fields anywhere in the tree    |

TypeScript support [#typescript-support]

`Selector<T>` is a template-literal type that computes every valid path for a given type.
Pass your data type as the generic argument and the selector string becomes type-checked:

```ts
type User = { name: string; address: { city: string; zip: string } };

// valid selectors inferred from User
new ExcludeFilter<User>(".address.city"); // ✓
new ExcludeFilter<User>(".address"); // ✓
new ExcludeFilter<User>(".missing"); // ✗ type error
```

Alternatively, let TypeScript infer the type from the value passed to a matcher:

```ts
const user = { name: "Alice", address: { city: "Paris", zip: "75001" } };

await expect(user).toJsonSnapshot({
  name: "user",
  redactions: [
    new ReplacedRedaction(".address.zip", "[ZIP]"), // type-checked against User
  ],
});
```

The type is resolved recursively up to a depth of **8 levels**. For `unknown` or `any` inputs
the selector falls back to an unconstrained string, accepting any valid segment sequence.

`NoInfer` and type inference [#noinfer-and-type-inference]

Selector arguments use `NoInfer<Selector<T>>` internally, which prevents TypeScript from
inferring `T` from the selector string itself. This forces `T` to be resolved from the object
being tested, keeping the constraint meaningful.

Examples [#examples]

```ts
// replace a top-level secret field
await expect({ name: "Alice", password: "s3cr3t" }).toJsonSnapshot({
  name: "user",
  redactions: [new ReplacedRedaction(".password", "[REDACTED]")],
});
// snapshot: { "name": "Alice", "password": "[REDACTED_1]" }
```

```ts
// replace every item in an array
await expect({ tokens: ["abc123", "def456"] }).toJsonSnapshot({
  name: "tokens",
  redactions: [new ReplacedRedaction(".tokens[]", "[TOKEN]")],
});
// snapshot: { "tokens": ["[TOKEN_1]", "[TOKEN_2]"] }
```

```ts
// replace a password at any depth in the object
await expect(deeplyNestedUser).toJsonSnapshot({
  name: "nested-user",
  redactions: [new ReplacedRedaction(".**.password", "[REDACTED]")],
});
```

```ts
// exclude a whole nested section
await expect(user).toJsonSnapshot({
  name: "user-public",
  filters: [new ExcludeFilter(".address")],
});
```

```ts
// sort a deeply-nested array of tags
await expect(post).toJsonSnapshot({
  name: "post",
  redactions: [new SortedRedaction(".**.tags")],
});
```
