Skip to content

fix(parse): type a column wrapped in parentheses - #338

Merged
tiagolauer merged 1 commit into
masterfrom
fix/288-paren-column
Aug 2, 2026
Merged

fix(parse): type a column wrapped in parentheses#338
tiagolauer merged 1 commit into
masterfrom
fix/288-paren-column

Conversation

@tiagolauer

Copy link
Copy Markdown
Owner

Fixes #288.

The bug

Query<DB, 'select (id) as x from users'>      // { x: unknown }[]
StrictRow<DB, 'select (id) as x from users'>  // { x: unknown }

Expected { x: number }. Strict mode even validates id successfully along the way and still returns unknown.

The fix

Two things:

  1. IsFunctionCall<'(id)'> returned true — the leading ${string} in ${string}(${string}) also matches the empty string, so the expression was typed as a call to a function named ''. It now requires a non-empty name.

  2. That alone leaves (id) to be looked up as a literal column name, so the parenthesized entry now unwraps a group that holds nothing but a column reference. Two groups are deliberately left alone: one carrying parens of its own (a nested call is still a call), and a scalar subquery, whose meaning is the query inside it — tests/scalar-subquery.test-d.ts caught that one going red mid-change.

The unwrap sits in the parenthesized-entry branch rather than in ResolveColumnType, so it costs nothing for ordinary columns.

Verification

tests/parenthesized-column.test-d.ts, eight cases. Four red on master:

tests/parenthesized-column.test-d.ts(18,3): error TS2344: Type 'false' does not satisfy the constraint 'true'.
tests/parenthesized-column.test-d.ts(22,3): ...
tests/parenthesized-column.test-d.ts(26,3): ...
tests/parenthesized-column.test-d.ts(30,3): ...
  • (id) as x in normal and strict mode, a qualified (u.name), and one among ordinary columns
  • a typo inside the parentheses still errors (unknown column: naem)
  • controls: count(*), lower(name) and a scalar subquery are unchanged

tsc --noEmit clean, 192 runtime tests pass, budget 194,776 (+2,290 — the unwrap plus the name check on paren-carrying expressions).

    Query<DB, 'select (id) as x from users'>      // was { x: unknown }[]
    StrictRow<DB, 'select (id) as x from users'>  // same

Two things had to be true for `(id)` to resolve, and neither was.

IsFunctionCall matched it, because the leading `${string}` in
`${string}(${string})` also matches the empty string, so the expression was
typed as a call to a function named `''`. It now requires a name.

That alone left `(id)` to be looked up as a literal column name, so the
parenthesized entry unwraps a group holding nothing but a column reference.
A group carrying parens of its own is left alone (a nested call is a call),
and so is a scalar subquery, which is a parenthesized group whose meaning is
the query inside it.

Strict mode's behaviour was the odd part worth naming: it resolved `id`
successfully on the way through and returned unknown anyway.

Fixes #288

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tiagolauer
tiagolauer force-pushed the fix/288-paren-column branch from 816e418 to 5026d56 Compare August 2, 2026 13:04
@tiagolauer
tiagolauer merged commit 9abd797 into master Aug 2, 2026
12 checks passed
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.

Parenthesized column (id) resolves to unknown via a phantom function call

1 participant