Skip to content

feat(compiler): allow declarations to be used as expressions#11019

Draft
timotheeguerin wants to merge 7 commits into
microsoft:mainfrom
timotheeguerin:decl-expr
Draft

feat(compiler): allow declarations to be used as expressions#11019
timotheeguerin wants to merge 7 commits into
microsoft:mainfrom
timotheeguerin:decl-expr

Conversation

@timotheeguerin

Copy link
Copy Markdown
Member

Summary

Allow model, enum, union, and scalar declarations to be used as expressions (e.g. the right-hand side of an alias, property types, return types, union variants).

In expression position these declarations:

  • are anonymous — their name is ""
  • produce a type with expression: true (so the type graph explicitly records that it came from an expression)
  • are not registered in the enclosing namespace
alias Foo = enum {
  a,
  b,
};

model Bar {
  status: enum { active, inactive };
  unit: scalar extends string;
  inner: model { x: string };
}

Named forms in expression position are also allowed (e.g. nested: model Inner { x: string }), and declarations can be nested.

Approach

Rather than introducing new *Expression AST node kinds, the existing statement node kinds are reused with an optional id (OptionallyNamedDeclarationNode), and these kinds are added to the Expression union. Whether a declaration is in statement vs expression position is determined by its parent node kind.

  • types.tsOptionallyNamedDeclarationNode (optional id); expression: boolean on Model/Enum/Scalar (Union already had it); the 4 statement kinds added to the Expression union.
  • parser.ts — optional-id parsing on the 4 declaration parsers; parsePrimaryExpression dispatch for the model/enum/union/scalar keywords; a scalar in expression position no longer consumes the alias trailing ;.
  • binder.ts — anonymous ("-" symbol) vs named binding depending on position.
  • checker.ts — derive name/expression; skip namespace registration for expression-form types; route expression decls through the namespace walk-up in getParentNamespaceType; new templated-declaration-in-expression diagnostic.
  • formatter — optional-id printing; avoids a double ; for anonymous scalars.
  • server/inspector — null-safe handling of the now-optional id.

Diagnostics

Template parameters on a declaration in expression position are rejected (templated-declaration-in-expression) since an anonymous declaration cannot be referenced or instantiated.

Tests

  • test/checker/declaration-expressions.test.ts (14 tests)
  • test/parser.test.ts — new "declaration expressions" block (11 cases)
  • test/formatter/formatter.test.ts — new "declaration expressions" block (5 cases)

Full compiler suite: 3982 passed / 6 skipped. tsc --noEmit, pnpm format, and pnpm lint are all clean.

Follow-ups (not in this PR)

  • Deeper TextMate grammar rules specific to keyword-declarations in expression position.
  • Policy for a name on a pure-expression declaration (currently kept on Type.name but not referenceable).
  • Validate downstream emitter packages against the new Expression union members.

Allow model, enum, union, and scalar declarations to be used in
expression position (e.g. alias RHS, property types). In expression
position they are anonymous (name is "") and the resulting type has
expression: true; they are not registered in the enclosing namespace.

A diagnostic is reported when template parameters are used on a
declaration in expression position.
@microsoft-github-policy-service microsoft-github-policy-service Bot added the compiler:core Issues for @typespec/compiler label Jun 18, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/compiler@11019
npm i https://pkg.pr.new/@typespec/html-program-viewer@11019

commit: 44e5e41

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @typespec/compiler
  • @typespec/html-program-viewer
Show changes

@typespec/compiler - feature ✏️

Allow model, enum, union, and scalar declarations to be used as expressions. A declaration used in expression position has its corresponding type marked with expression: true and is not registered in the enclosing namespace. It may be named or anonymous (in which case its name is "").,> ,> tsp,> alias Foo = enum {,> a,,> b,,> };,> ,> model Bar {,> status: enum { active, inactive };,> unit: scalar extends string;,> inner: model Inner { x: string };,> },>

@typespec/html-program-viewer - feature ✏️

Display the new expression property on Model, Enum, and Scalar types in the program viewer.

@azure-sdk-automation

azure-sdk-automation Bot commented Jun 18, 2026

Copy link
Copy Markdown

You can try these changes here

🛝 Playground 🌐 Website 🛝 VSCode Extension

A keyword-form union (`union { a, b }`) used in expression position is marked
`expression: true`, which caused checkUnionExpression to flatten its (possibly
named) variants into the parent union, silently dropping colliding members.
Flatten only unions originating from the `|` operator (UnionExpression node).
Add tests for:
- expression: false on statement declarations
- name retention on named declaration expressions
- named expressions not being referenceable
- union namespace non-registration
- alias-resolved types, op return/param, union variant usage
- member access via alias, decorator rejection
- enum values, union named variants, scalar constructors, model spread
- parser negatives for interface/op in expression position
- formatter named & nested declaration expressions
Anonymous declarations used in expression position rendered with a stray
namespace prefix (e.g. `Ns.` for enum/scalar, `Ns.{ x: string }` for
keyword-form model). Render them inline and un-prefixed, mirroring union
expression naming.

Also extract a single shared `isDeclarationInExpressionPosition` helper used
by both the binder and checker so the two position predicates cannot drift,
and add regression tests (type names, keyword-form union as `|` operand,
template parameter referenced inside an expression declaration).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

compiler:core Issues for @typespec/compiler ui:type-graph-viewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant