Skip to content

refactor(tests): drop the no-unused-expressions suppression - #88

Merged
Upd4ting merged 1 commit into
mainfrom
refactor/burn-down-suppressions
Nov 13, 2026
Merged

Upd4ting merged 1 commit into
mainfrom
refactor/burn-down-suppressions

Conversation

@Upd4ting

@Upd4ting Upd4ting commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Why

Antony wants the lint in this repo to hold with no escape hatches, so every suppression guardrails.suppressions.json allows has to go (ANT-1103). This PR removes the one suppression that can go without touching a published declaration or runtime behaviour. It also lowers the budget so that suppression cannot come back.

What

  • src/tests/merge_operations.test.ts loses its file-wide /* eslint-disable @typescript-eslint/no-unused-expressions */. It hid four chai property assertions (expect(x).to.not.be.undefined;). Each one is now the call expect(x).to.not.equal(undefined);.
  • guardrails.suppressions.json lowers @typescript-eslint/no-unused-expressions from 1 to 0 (pnpm run suppressions:update).

No SAFETY: comment is added.

Proof

Old and new assertions agree on every value that matters. Command:
node -e '…expect(v).to.not.be.undefined vs expect(v).to.not.equal(undefined)…'

undefined old: fail new: fail
{"a":1} old: pass new: pass
null old: pass new: pass

So each test still fails on the wrong implementation it targets: a merge that drops the matching order, which makes find return undefined.

pnpm run checks: Checks: 11 passed, 0 failed.

API: 1 packages, 21 entries unchanged against origin/main (committed HEAD).
API: skipped .agents/skills/verify-interface, private.
Runtime API: 1 packages, 11 entries unchanged against origin/main (committed HEAD).
No behavioural probe ran.

Suppressions in authored code files (origin/main and committed HEAD). A lint directive counts once per rule it names; * is a directive naming none.

Suppressions base HEAD
Lint suppressions, all rules 5 4
Lint suppressions, @typescript-eslint/no-unused-expressions 1 0
Lint suppressions, anti-slop/no-chained-type-assertions 1 1
Lint suppressions, typescript/no-unnecessary-type-parameters 1 1
Lint suppressions, typescript/no-unused-vars 1 1
Lint suppressions, unicorn/no-thenable 1 1
@ts-expect-error 2 2
@ts-ignore 0 0
SAFETY: comments 5 5
Lint config rule-offs, all rules 0 0

Left in place

These suppressions stay, by decision on ANT-1103. Removing any of them changes a published declaration or runtime behaviour, which is not a cleanup. Items 1, 2 and 4 wait on Antony's published-API decision, item 3 stays and is listed, and item 5 waits on his runtime-refusal-tests decision.

  1. The SAFETY: comments on cast<U>() in src/staged-query/stream.ts, datum.ts and valueproxy.ts, plus the anti-slop/no-chained-type-assertions directive still on the valueproxy.ts one. The method only relabels a type argument of the same object. TypeScript refuses a single as between X<T> and an unrelated X<U>. Returning a fresh object with the same stages would change the documented "same stream" identity.
  2. typescript/no-unused-vars and typescript/no-unnecessary-type-parameters on class Query<T> in src/staged-query/query.ts. T is consumed through declaration merging in src/query.ts. Any use inside the class shows in the published .d.ts.
  3. unicorn/no-thenable on then in src/query.ts. Query is PromiseLike on purpose, so await query runs it. This repo holds one case, too few for a tooling-configs rule request.
  4. @ts-expect-error (2) in src/tests/atomic_mutation.test.ts. These feed type-forbidden values to check a runtime refusal, a case already with Antony.
  5. SAFETY: in Schema.get (src/staged-query/schema.ts) and callfunc (src/staged-query/common.ts). The caller picks the type arguments, which nothing stores at runtime. A type-level fix would change the published or protected signature.

@Upd4ting
Upd4ting force-pushed the refactor/burn-down-suppressions branch from 7ec3628 to a515f10 Compare November 13, 2026 09:48
@Upd4ting
Upd4ting merged commit 238d656 into main Nov 13, 2026
8 checks passed
@Upd4ting
Upd4ting deleted the refactor/burn-down-suppressions branch November 13, 2026 10:01
Upd4ting added a commit that referenced this pull request Nov 13, 2026
)

## Why

Antony wants the lint in this repo to hold with no escape hatches
(ANT-1103). The last two `@ts-expect-error` comments sat in
`src/tests/atomic_mutation.test.ts`. They let tests hand
`ValidateAtomicMutation` values its types forbid, to prove it refuses
them at runtime. Antony's decision (option E) moves those checks into a
plain JavaScript spec that calls the built module. No `@ts-expect-error`
is left. Stacked on #88.

## What

- `src/tests/atomic_mutation.untyped.spec.js` is new. It holds the two
loops that fed off-contract values, word for word: `expectedRevision` of
`null`, `undefined`, `""` or `{ kind: "missing", extra: true }`, and
`expectedValue` of `null`, `undefined`, `{}`, `[]`, `NaN` or `new
Date(NaN)`. Each must throw `TypeError`. It `require`s `../index`, which
in `dist/tests` is the built module.
- `src/tests/atomic_mutation.test.ts` drops those two loops and both
`@ts-expect-error` comments. Its typed checks stay.
- `tsconfig.json` sets `allowJs`, so `tsc` emits the spec into
`dist/tests`, where `ajs module test` picks it up
(`/\.(test|spec)\.(js|ts)$/`). The spec ships with the conformance
suite, so database modules still run these checks.
- `knip.config.ts` lists `src/tests/**/*.untyped.spec.js` as a
conformance entry.
- `guardrails.suppressions.json`: `tsExpectError` 2 → 0.

No `SAFETY:` comment is added.

## Proof

The new spec, with the TS suite it was split from (`ajs module test . -f
dist/tests/atomic_mutation.untyped.spec.js -f
dist/tests/atomic_mutation.test.js`):

```
  Atomic mutation runtime refusal of off-contract values
    ✔ refuses a null, undefined, empty or extended expected revision
    ✔ refuses a non-scalar or invalid expected value
  ...
  12 passing (59ms)
```

Each spec fails on the wrong implementation it targets. I patched
`dist/staged-query/atomic.js` and restored it after each run:

- Validator accepts a `null` or `undefined` expected revision:

  ```
      1) refuses a null, undefined, empty or extended expected revision
      ✔ refuses a non-scalar or invalid expected value
    1 passing (5ms)
    1 failing
    AssertionError: expected [Function] to throw TypeError
  ```

- Equality check accepts `null` and objects:

  ```
      ✔ refuses a null, undefined, empty or extended expected revision
      1) refuses a non-scalar or invalid expected value
    1 passing (37ms)
    1 failing
    AssertionError: expected [Function] to throw TypeError
  ```

```mermaid
flowchart LR
  A[src/tests/atomic_mutation.untyped.spec.js] -->|tsc allowJs| B[dist/tests/atomic_mutation.untyped.spec.js]
  B -->|require ../index| C[dist/index.js ValidateAtomicMutation]
  D[ajs module test] -->|spec.js pattern| B
```

`pnpm run checks`: `Checks: 11 passed, 0 failed.`

API: 1 packages, 21 entries unchanged against
refactor/burn-down-suppressions (committed HEAD).
API: skipped .agents/skills/verify-interface, private.
Runtime API: 1 packages, 11 entries unchanged against
refactor/burn-down-suppressions (committed HEAD).
No behavioural probe ran.

Suppressions in authored code files (refactor/burn-down-suppressions and
committed HEAD). A lint directive counts once per rule it names; `*` is
a directive naming none.
| Suppressions | base | HEAD |
| --- | ---: | ---: |
| Lint suppressions, all rules | 4 | 4 |
| Lint suppressions, `anti-slop/no-chained-type-assertions` | 1 | 1 |
| Lint suppressions, `typescript/no-unnecessary-type-parameters` | 1 | 1
|
| Lint suppressions, `typescript/no-unused-vars` | 1 | 1 |
| Lint suppressions, `unicorn/no-thenable` | 1 | 1 |
| `@ts-expect-error` | 2 | 0 |
| `@ts-ignore` | 0 | 0 |
| `SAFETY:` comments | 5 | 5 |
| Lint config rule-offs, all rules | 0 | 0 |
<!-- antelopejs-proof base=a515f10e7c286ab9a977db7930c5ca23aafca081
head=56345c4a690fb4d65ca40b0bed9b321caece8917 lines=16
digest=db932b8f5f893567 command: antelopejs-api-diff
refactor/burn-down-suppressions -->
Upd4ting added a commit that referenced this pull request Nov 13, 2026
…hema lookups (#91)

## Why

Antony wants the lint in this repo to hold with no escape hatches
(ANT-1103). #88 and #90 removed every suppression that could go without
a published change. The rest sat on published signatures, and Antony
approved one breaking PR for them. With it, every ceiling in
`guardrails.suppressions.json` is 0: no `oxlint-disable`,
`@ts-expect-error` or `SAFETY:` comment is left anywhere in the repo. It
must ship as **0.4.0**, because database modules cap their range below
the next minor.

## Breaking changes

Each item is in `UPGRADING.md` under `## v0.4.0`, with a before and
after snippet.

1. **A query is no longer a promise.** `then` is gone from
`Query.prototype`, and `QueryExecution` no longer extends `PromiseLike`.
`await query` gives back the query object and runs nothing. Callers use
`.run()`, `cursor()` or `for await`. This removes the
`unicorn/no-thenable` directive. A type-aware codemod moved every `await
<query>` in `src/tests`, `docs/`, `README.md`, `skills/` and
`.agents/skills/verify-interface` to `.run()`, and lint
(`typescript/await-thenable`) reports none left.
2. **`cast<U>()` returns a new object with the same stages** on `Datum`,
`Stream` and `ValueProxy`. It used to return `this` through `as unknown
as`. A new constructor overload, `new X(undefined, previous)`, copies
`previous`'s stages and records none. This removes 3 `SAFETY:` comments
and 1 `anti-slop/no-chained-type-assertions` directive. On a `Table`,
`Selection` or `SingleSelection`, `cast()` now returns a plain `Stream`
or `Datum`, the class its type already declared.
3. **`Schema.get(id)` takes no type arguments** and returns `Schema<any,
unknown> | undefined`, which is what the registry holds. This removes 1
`SAFETY:` comment. Docs that called `Schema.get<{ users: User }>(…)` now
use the schema object the constructor returned.
4. **Protected `callfunc` takes 1 or 2 typed constructors** (two
overloads) and calls `func` without a tuple cast. This removes 1
`SAFETY:` comment. The recorded stages are byte-identical, with argument
ids still allocated a, then b. `Stream#group` now passes the
`ValueProxy` type argument its mapper declares, which only changes the
type.
5. **The build-only `Query<T>` now uses `T`.** This removes the
`typescript/no-unused-vars` and
`typescript/no-unnecessary-type-parameters` directives, and fixes a real
type hole. Loaded from `staged-query` alone, `Query<string>` was
assignable to `Query<number>`, and `ExtractType<Datum<number>>` was
`unknown`. The class declares `declare protected readonly [YIELDS]?:
QueryYield<T>`.

## The `Query<T>` member, for the adversarial check

It is a declared-only member: `declare` emits nothing, and no instance
ever has it. In the build-only entry, a query holds stages and never a
value of type `T`. So any member using `T` is declared, not stored. Two
reviewers (Opus and Sonnet) each judged it, and both found it a real
fix, not a brand. They ran the type probe added to
`scripts/test-staged-query.cjs` against main's build, and all four
assertions fail there. Neither found a fix without a member:
- Matching `Datum<infer A>` / `Stream<infer A>` in `ExtractType` still
leaves `T` unused, and loses bare `Query<T>`.
- Merging `QueryExecution<T>` into the build-only entry would give it a
`run()` it does not have.
- `T` cannot be dropped, because declaration merging with `src/query.ts`
needs the same type parameters.

The key is `YIELDS`, a `declare const YIELDS: unique symbol` no other
module can name, so a subclass property named `yields` does not collide
with it.

No `SAFETY:` comment is added.

## Proof

New tests at `origin/main` (55853ef), with the head's test files copied
in:

```
  1) Awaiting a query gives back the query and runs nothing
  2) Casts a stream to a new stream with the same stages
  3) Casts a datum to a new datum with the same stages
  4) Casts a proxy to a new proxy with the same stages
  7 passing (13ms)
  4 failing
  1) Query execution
      AssertionError: expected true to equal false
  2) Recorded stages
      AssertionError: expected Table{ stages: [ { …(3) }, …(2) ] } to not equal Table{ stages: [ { …(3) }, …(2) ] }
src/tests/basic_operations.test.ts(51,9): error TS2322: Type 'true' is not assignable to type 'false'.   (Schema get claims no definition type)
AssertionError [ERR_ASSERTION]: staged-query types read T back from queries   (scripts/test-staged-query.cjs)
```

At head:

```
    ✔ Awaiting a query gives back the query and runs nothing
    ✔ Casts a stream to a new stream with the same stages
    ✔ Casts a datum to a new datum with the same stages
    ✔ Casts a proxy to a new proxy with the same stages
    ✔ Schema get claims no definition type
  28 passing (93ms)
staged-query purity, AQL stages, and root compatibility: PASS
```

- `pnpm run checks`: `Checks: 11 passed, 0 failed.`
- The docs type-check (`tsc -p docs/tsconfig.json`) is clean.

```mermaid
flowchart LR
  subgraph Before
    A1[await query] -->|then| R1[RunQuery]
    C1["cast()"] -->|as unknown as| S1[same object]
  end
  subgraph After
    A2[await query] --> Q2[the query object, nothing runs]
    B2["query.run()"] --> R2[RunQuery]
    C2["cast()"] -->|"new X(undefined, this)"| S2[new object, same stages]
  end
```

### verify-interface

A thread that did not write the change ran
`.agents/skills/verify-interface` at 452d96c. All 30 journeys the
harness registers pass. The `query-execution` evidence:

```
{"lazy":0,"awaitedIsQuery":true,"awaitRuns":0,"viaRun":[{"title":"A"}],"runs":3,"sameStages":true,"rejected":"Error: provider refused","onRejected":"caught:provider refused","methods":["function","undefined","function"],"iterator":"function"}
```

`minor-bump` and `every-function` did not run. `features/smoke.md`
deliberately leaves both unregistered in this repo. The only later
commit, b95e426, keeps `StagedObject.length` at 2 and moves the argument
helper to module scope. `pnpm run checks` and `pnpm test:staged-query`
cover it, and both pass.

### API diff

`antelopejs-api-diff origin/main` exits 1 here, as expected for a
breaking PR. Its full output is 137 KB, more than twice GitHub's
65,536-character body limit, so it is not pasted. To reproduce: `pnpm
exec antelopejs-api-diff origin/main` at the head of this PR. Its
closing stamp then reads `base=55853ef… head=b95e426… lines=3837
digest=c2620097c6c135f4`. What it reports:

- Declarations (repeated in each entry and `typesVersions` file):
- `Query<T>` gains the `[YIELDS]` member and the new `QueryYield<T>`
interface.
- `QueryExecution<T>` drops `PromiseLike<T>` and types `cursor()` from
`QueryYield<T>["item"]`.
  - `Schema.get` loses its type parameters.
- `StagedObject` gains the `(undefined, previous)` constructor overload.
  - `callfunc` becomes two overloads.
- Runtime:
- `then` is removed from `Query.prototype`, in the root, `query` and
`main` entries.
  - `StagedObject.prototype.callfunc.length` goes from 1 to 0.
  - Nothing else: `StagedObject.length` stays 2, and no static is added.
- Suppressions: `Lint suppressions, all rules` 4 → 0 and `SAFETY:`
comments 5 → 0. Every other row was already 0.

## Downstream

Every module or app that awaits a query must call `.run()` once it moves
to 0.4.0. From reading their source, these also stop compiling:
- dms: `getTenantSchema(): Schema` returning `Schema.get(…)`.
- dms-database: `Object.hasOwn(schema.definition, …)`, and its console's
`Schema.get<unknown, SchemaDefLike>`.

`UPGRADING.md` points root callers to `Schema.list().get(id)`, which
stays typed with `SchemaDefinition`.
Upd4ting added a commit that referenced this pull request Nov 13, 2026
## Why

A user upgrading `@antelopejs/interface-database` to 0.4.0 should learn
from its release notes which changes break their code and how to adapt.
Today the `## v0.4.0` section of `CHANGELOG.md` and the v0.4.0 GitHub
release name #91 only as "Drop casts, thenable queries and caller-typed
schema lookups". Its upgrade notes live in `UPGRADING.md`. The release
commit (7268503) does not copy them, so this PR does, as AGENTS.md asks.

## What

This adds a `### Upgrade notes` block under `## v0.4.0` in
`CHANGELOG.md`, right after the compare link, where the v0.3.0 block
sits. It holds the `UPGRADING.md` v0.4.0 bullets verbatim, snippets
included. It lists no implementer action: database modules implement
nothing new, and the conformance suite in `dist/tests` already runs
every query with `run()`.

## Pending step for Antony

I have not edited the v0.4.0 GitHub release body. Paste the text under
**Release notes to paste** into
https://github.com/AntelopeJS/interface-database/releases/tag/v0.4.0. It
is the current release body with the same upgrade-notes block after the
compare link.

## Release notes to paste

````markdown
[compare changes](v0.3.3...v0.4.0)

### Upgrade notes

- **A query is no longer a promise.** `Query` has no `then()`, and `QueryExecution` no longer extends `PromiseLike`. `await query` gives back the query object and sends nothing to the provider. Run a query with `run()`, or read it with `cursor()` or `for await`. A query in `Promise.all([...])`, or returned from an `async` function, is no longer run either. TypeScript rejects a query passed where a `PromiseLike` is expected, and the `typescript/await-thenable` lint rule reports `await` on a query and a query passed to `Promise.all`, `Promise.race` or another promise aggregator. The conformance suite in `dist/tests` runs every query with `run()`. A database module implements nothing new.

  ```ts
  // Before
  const adults = await users.filter((u) => u.key("age").ge(18));
  // After
  const adults = await users.filter((u) => u.key("age").ge(18)).run();
  ```

- **`cast()` returns a new object.** `Datum#cast`, `Stream#cast` and `ValueProxy#cast` return a new object with the same stages, built by the new `StagedObject` constructor form `new X(undefined, previous)`, which records no stage. `cast()` on a `Table`, a `Selection` or a `SingleSelection` returns a plain `Stream` or `Datum`, the class its type already declared. Code that compared the result with its receiver, or relied on it keeping the subclass methods at runtime, such as a table's `insert` or `getAll`, keeps the receiver instead.

  ```ts
  // Before: the same object, still a Table at runtime
  table.cast<Row>() === table; // true
  // After: a new Stream with the same stages
  table.cast<Row>() === table; // false
  table.cast<Row>().build(); // equal to table.build()
  ```

- **`Schema.get()` takes no type arguments.** The registry keeps none, so `Schema.get(id)` returns `Schema<any, unknown> | undefined`: tables hold `any` documents, and the definition is typed `unknown`. A typed lookup such as `Schema.get<{ users: User }>(id)` fails to compile (TS2558). Query typed tables through the schema object the constructor returned. On the runtime entry, `Schema.list().get(id)` returns the schema with its definition typed `SchemaDefinition`.

  ```ts
  // Before
  const schema = Schema.get<{ users: User }>("myapp")!;
  // After: export the schema you define, and import it
  export const schema = new Schema<{ users: User }>("myapp", definition);
  ```

- **The build-only `Query<T>` keeps its result type.** Loaded from `@antelopejs/interface-database/staged-query` alone, `Query<T>` now uses `T`, so `ExtractType` reads the result type of a `Datum` or a `Stream` a mapper returns, and a `Query<string>` is no longer assignable to a `Query<number>`. Code that relied on that assignment fails to compile.

  ```ts
  // Before, build-only entry: compiled, typed unknown
  const q: Query<number> = schema.createInstance(); // Query<string>
  // After: TS2322
  ```

- **`StagedObject#callfunc` checks its argument classes.** The protected method takes one or two `StagedConstructor`s, and each must build the parameter type `func` declares. A subclass that passed `ValueProxy<unknown>` for a parameter typed `ValueProxy<T>` passes `ValueProxy<T>`.

  ```ts
  // Before
  this.callfunc(mapper, Stream<T>, ValueProxy<unknown>);
  // After
  this.callfunc(
    mapper,
    Stream<T>,
    ValueProxy<K extends keyof T ? T[K] : unknown>,
  );
  ```

### 🚀 Enhancements

- **staged-query:** ⚠️  Drop casts, thenable queries and caller-typed schema lookups ([#91](#91))

### 💅 Refactors

- **tests:** Drop the no-unused-expressions suppression ([#88](#88))

### 📖 Documentation

- **readme:** List the mysql and postgres backends among the implementations ([#89](#89))

### ✅ Tests

- **atomic:** Move off-contract refusal checks into an untyped spec ([#90](#90))

### 🤖 CI

- **release:** Accept an exact release-version on dispatch ([#92](#92))

#### ⚠️ Breaking Changes

- **staged-query:** ⚠️  Drop casts, thenable queries and caller-typed schema lookups ([#91](#91))

### ❤️ Contributors

- Antony Rizzitelli ([@Upd4ting](http://github.com/Upd4ting))
````

## Proof

The inserted block matches `UPGRADING.md`'s v0.4.0 block line for line:

```
$ diff <(grep -v '^$' notes-from-UPGRADING.md) <(grep -v '^$' block-in-CHANGELOG.md) && echo verbatim
verbatim
```

CHANGELOG diff:

````diff
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bb189cc..d22fdb7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,57 @@
 
 [compare changes](v0.3.3...v0.4.0)
 
+### Upgrade notes
+
+- **A query is no longer a promise.** `Query` has no `then()`, and `QueryExecution` no longer extends `PromiseLike`. `await query` gives back the query object and sends nothing to the provider. Run a query with `run()`, or read it with `cursor()` or `for await`. A query in `Promise.all([...])`, or returned from an `async` function, is no longer run either. TypeScript rejects a query passed where a `PromiseLike` is expected, and the `typescript/await-thenable` lint rule reports `await` on a query and a query passed to `Promise.all`, `Promise.race` or another promise aggregator. The conformance suite in `dist/tests` runs every query with `run()`. A database module implements nothing new.
+
+  ```ts
+  // Before
+  const adults = await users.filter((u) => u.key("age").ge(18));
+  // After
+  const adults = await users.filter((u) => u.key("age").ge(18)).run();
+  ```
+
+- **`cast()` returns a new object.** `Datum#cast`, `Stream#cast` and `ValueProxy#cast` return a new object with the same stages, built by the new `StagedObject` constructor form `new X(undefined, previous)`, which records no stage. `cast()` on a `Table`, a `Selection` or a `SingleSelection` returns a plain `Stream` or `Datum`, the class its type already declared. Code that compared the result with its receiver, or relied on it keeping the subclass methods at runtime, such as a table's `insert` or `getAll`, keeps the receiver instead.
+
+  ```ts
+  // Before: the same object, still a Table at runtime
+  table.cast<Row>() === table; // true
+  // After: a new Stream with the same stages
+  table.cast<Row>() === table; // false
+  table.cast<Row>().build(); // equal to table.build()
+  ```
+
+- **`Schema.get()` takes no type arguments.** The registry keeps none, so `Schema.get(id)` returns `Schema<any, unknown> | undefined`: tables hold `any` documents, and the definition is typed `unknown`. A typed lookup such as `Schema.get<{ users: User }>(id)` fails to compile (TS2558). Query typed tables through the schema object the constructor returned. On the runtime entry, `Schema.list().get(id)` returns the schema with its definition typed `SchemaDefinition`.
+
+  ```ts
+  // Before
+  const schema = Schema.get<{ users: User }>("myapp")!;
+  // After: export the schema you define, and import it
+  export const schema = new Schema<{ users: User }>("myapp", definition);
+  ```
+
+- **The build-only `Query<T>` keeps its result type.** Loaded from `@antelopejs/interface-database/staged-query` alone, `Query<T>` now uses `T`, so `ExtractType` reads the result type of a `Datum` or a `Stream` a mapper returns, and a `Query<string>` is no longer assignable to a `Query<number>`. Code that relied on that assignment fails to compile.
+
+  ```ts
+  // Before, build-only entry: compiled, typed unknown
+  const q: Query<number> = schema.createInstance(); // Query<string>
+  // After: TS2322
+  ```
+
+- **`StagedObject#callfunc` checks its argument classes.** The protected method takes one or two `StagedConstructor`s, and each must build the parameter type `func` declares. A subclass that passed `ValueProxy<unknown>` for a parameter typed `ValueProxy<T>` passes `ValueProxy<T>`.
+
+  ```ts
+  // Before
+  this.callfunc(mapper, Stream<T>, ValueProxy<unknown>);
+  // After
+  this.callfunc(
+    mapper,
+    Stream<T>,
+    ValueProxy<K extends keyof T ? T[K] : unknown>,
+  );
+  ```
+
 ### 🚀 Enhancements
 
 - **staged-query:** ⚠️  Drop casts, thenable queries and caller-typed schema lookups ([#91](#91))
````

`pnpm run checks`:

```
Checks: 11 passed, 0 failed.
```

The CI docs check reads only `README.md` and `docs/`, so this PR does
not change its inputs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant