Auto decorators#10197
Conversation
commit: |
|
All changed packages have been documented.
Show changes
|
|
You can try these changes here
|
|
I generally like the idea of the feature but I have a couple of reservations:
On the questions:
|
|
Design meeting descision:
|
…plicate handling - Single-arg tspd typed accessor now unwraps the stored record to the bare value, preserving parity with hand-written extern getters - Auto decorator duplicate detection reuses validateDecoratorUniqueOnNode (identity match, warning) and no longer early-returns, so duplicates are last-write-wins like extern decorators
- Add tests: hasAutoDecorator true/false, getAutoDecoratorValue undefined when absent, `is` inheritance, non-Model (ModelProperty) target, auto rejected on model/function declarations, namespaced tspd accessor FQN - Restrict function declarations to extern|internal modifiers so the auto modifier is no longer silently accepted on functions
Auto decorators
Summary
Adds an
automodifier for decorator declarations. Auto decorators are simple metadata annotations that auto-store their arguments — no JavaScript implementation needed.Motivation
A large number of decorators in the TypeSpec ecosystem follow the same pattern: accept arguments, store them in a state map, expose a getter. This requires boilerplate across three files (
.tspdeclaration,.tsimplementation,.tsstate accessors). Auto decorators eliminate this entirely.Design
Syntax
autois a new modifier keyword ondecdeclarations, mutually exclusive withextern:Auto decorators are gated behind the
auto-decoratorscompiler feature. Declaring one without enabling the feature intspconfig.yamlis an error.Storage
The compiler auto-generates an implementation that stores args in
program.stateMapkeyed bySymbol.for("TypeSpec.dec:<fqn>"). The value is always a record keyed by parameter name (this uniformity keeps the generic read API simple and enables seamless migration to/from anexternimplementation that shares the same state key):{}{ paramName: value }{ paramName1: val1, paramName2: val2 }Applying the same auto decorator twice on the same declaration emits a
duplicate-decoratorwarning (via the sharedvalidateDecoratorUniqueOnNodehelper) but still stores — duplicate resolution is last-write-wins, matching extern decorators.Compiler API
Generic functions to read auto decorator state by FQN string — no generated code needed:
getAutoDecoratorValuealways returns the raw stored record (e.g.{ value: "x" }).tspd generation
tspd gen-extern-signaturegenerates typed accessors for auto decorators. These are ergonomic sugar over the generic API: the single-argget*unwraps the record to the bare value so it stays a drop-in replacement for hand-written extern getters, multi-arg returns the record, and no-arg returns a booleanis*:Decorator type
The
Decoratorruntime type gains adeclarationKind: "extern" | "auto"property (extensible for future kinds).autoas a modifier keywordautois a modifier keyword ondecdeclarations.Open Questions
model is/op is: Auto decorators inherit throughisexactly like extern decorators (decorators are re-applied to the copy); no special-casing.Migration Examples
Several existing decorators across the TypeSpec ecosystem are pure "store args in state" and could be migrated to
auto dec:Compiler —
@discriminatorBefore:
After:
No JS needed.
HTTP —
@body,@bodyRoot,@bodyIgnore,@statusCode,@multipartBodyThese are all boolean flags — the decorator just marks the target:
Before:
After:
Same pattern applies to
@bodyRoot,@bodyIgnore,@statusCode,@multipartBody.Not candidates
Decorators that do validation, normalization, or side effects beyond storage are not candidates. Examples:
@doc(template string rewriting),@format(uniqueness validation),@pattern(regex validation),@route(shared route handling),@server(URL parameter parsing).