Skip to content

fix(parse): keep the alias when an expression continues past a group - #339

Merged
tiagolauer merged 1 commit into
masterfrom
fix/290-paren-expression-alias
Aug 2, 2026
Merged

fix(parse): keep the alias when an expression continues past a group#339
tiagolauer merged 1 commit into
masterfrom
fix/290-paren-expression-alias

Conversation

@tiagolauer

Copy link
Copy Markdown
Owner

Fixes #290.

The bug

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

Expected { x: unknown }[] — the value being unknown is fine, the key is the bug.

The fix

The paren-entry branch assumed whatever follows the closing paren is an alias and returned [Trim<After>, Expr] directly. The bare-entry branch guards the same situation with IsOperatorExpression; this branch had no such check.

When the tail is operator-shaped, the entry is now split at its top-level AS — the same signal the rest of the parser uses — so (id + 1) * 2 as x gives alias x and expression (id + 1) * 2.

Without an AS there is nothing to tell an alias from the rest of the expression, so the whole entry becomes the key ('(id + 1) * 2 x'). That is the answer the bare-entry branch gives for the same shape, and better than the old '* 2 x', which named a fragment. That case is pinned by a test.

Verification

tests/paren-expression-alias.test-d.ts, eight cases. Four red on master:

tests/paren-expression-alias.test-d.ts(17,3): error TS2344: Type 'false' does not satisfy the constraint 'true'.
tests/paren-expression-alias.test-d.ts(25,3): ...
tests/paren-expression-alias.test-d.ts(29,3): ...
tests/paren-expression-alias.test-d.ts(33,3): ...
  • * 2 as x, / 2 as average, the no-AS form, and one among ordinary columns

Controls: (id + 1) as next, (id + 1) next, a scalar subquery with an alias, and count(*) as total are all unchanged.

tsc --noEmit clean, budget 192,636 (+150).

    Query<DB, 'select (id + 1) * 2 as x from users'>
    // was { '* 2 as x': unknown }[]

The paren-entry branch returned whatever followed the closing paren as the
alias. That holds for `(id + 1) as next`, and not at all for an expression
that carries on past the group, where the tail is part of the expression and
the alias sits at the end of it.

When the tail is operator-shaped, the entry is now split at its top-level
AS, the same signal the rest of the parser uses; the bare-entry branch has
guarded this case with IsOperatorExpression all along. Without an AS there
is nothing to tell an alias from the rest of the expression, so the whole
entry becomes the key - the answer the bare-entry branch gives too, and
better than naming a fragment of it.

The value stays `unknown`: an arithmetic expression is not typed. Only the
key was wrong.

Fixes #290

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tiagolauer
tiagolauer force-pushed the fix/290-paren-expression-alias branch from bd6a30e to 4f31ab9 Compare August 2, 2026 13:04
@tiagolauer
tiagolauer merged commit 40f0034 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.

Expression continuing after a parenthesized group is swallowed into the alias

1 participant