tc39/proposal-decorators

Proposal suggestion: an extension for annotation decorators

Closed this issue · 2 comments

Proposal: Metadata Annotations

From: https://github.com/matthew-dean/proposal-annotations

In the TC39 decorators proposal, there is a draft document proposing annotation syntax that this proposal extends from. Note that the scope of this document is limited to metadata annotations, and not decorators in general. For a description of how decorators that execute code at runtime might work for some of these, please see the TC39 extensions proposal.

The TC39 proposed syntax is as follows:

@{x: "y"}
function f() { }

It starts with @ followed by an object. And is proposed to be referenced with something like

f[Symbol.annotations][0].x  // "y"

This proposal's syntax

This proposal starts with similar assumptions:

  1. We should be able to decorate almost any function, parameter, or variable declaration with any annotation.
  2. This decoration should be very minimal.
  3. The annotation decorations should have a predictable structure (an array).
  4. Metadata annotation decorations should be referenceable at runtime with minimal syntax.

Following the above guidelines, this proposal adds two forms of annotations by allowing strings and arrays as annotations, and not just objects:

// existing syntax
@{ x: 'y' } 
// new syntax
@'x' // or double-quoted @"x" or as a template string @`x`
@['x']

Also, this proposal tries to keep the symbol consistent the symbol to retrieve metadata annotations (Symbol.metadata).

This would result in the following change:

@'x'
function f() { }

f[Symbol.metadata][0] // "x"

Note that annotations are always a collection (an Array), because you can add multiple annotations.For example, a type-checking system might want to do something like:

function f(@'number' @'string' p1) { }

f[Symbol.metadata][0] // 'number'
f[Symbol.metadata][1] // 'string'

Parameter annotations

Like the TC39 extensions, as seen in the example above, parameters can be decorated (and have metadata annotations), like this:

function f(@'x' arg) { return arg * 2 }

You could access the above like:

// access the first parameter, and the first annotation
f[Symbol.metadata].parameters[0][0]  // 'x'

Let decorators

Variables can also have attached metadata.

let @'num' x = 1;

x[Symbol.metadata][0] // 'num'

Note that the above type annotation would have no effect at runtime, but could allow a type-checking system to enforce the type through static analysis (or runtime helpers).

Const decorators

One can also attach metadata to a const.

const @'num' x = 1;

Value annotations

We can add metadata to something like a string expression or any other value. Sometimes, like in the following example, this might have no meaning at runtime, but could be used by a type-checking system as, say, file definitions:

@'filetype' 'flow';

At runtime, this would be the equivalent of evaluating a plain string expression, like:

'flow';

Technically, if this string were assigned to a variable, it would have the metadata ['filetype']

Similarly (and perhaps more usefully), we could annotate a value like:

let x = @'number' 5

The decorators proposal says that values can get wrapped in functions and returned, but this proposal simply adds [Symbol.metadata]: ['number'].

This can be useful for type-checking "assertions" in TypeScript / Flow.

How could we use this proposal?

The simplest way to use this metadata annotations is to provide "structured documentation", but probably one of the most powerful ways to use this proposal is for type-checking systems.

You can check out an example of how this might be used as a replacement for Microsoft's type annotation proposal here.

This discussion may be more relevant over in https://github.com/tc39/proposal-decorator-metadata, this proposal is not going to include any major new features or changes at this point. I'm going to close this issue, but feel free to reopen over there for discussion.

I think that this style of annotation could possibly be useful for the type annotations that typescript is looking to emit (cc @rbuckton) but its usage of Symbol.metadata would conflict with the other metadata system's usage of that symbol. There are many more use cases for dynamic metadata than static metadata, so if we have to choose between the two I would choose dynamic (e.g. the one outlined by https://github.com/tc39/proposal-decorator-metadata). If there is a way to expose this information without the conflict though I could see it being useful.

@pzuraq Thank you! I have opened an issue here: tc39/proposal-decorator-metadata#11