Skip to content

Annotations

The typescript template set reads EAnnotation details from the .ecore file. In the file, an annotation looks like this:

<eOperations name="describe" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString">
<eAnnotations source="https://typemf.dev/generator">
<details key="body" value="return this.getTitle() + ' by ' + this.getAuthor()?.getName();"/>
</eAnnotations>
</eOperations>
Source Key On Meaning
https://typemf.dev/generator documentation any element TSDoc comment
https://typemf.dev/generator body operation TypeScript method body
https://typemf.dev/generator dispatch overloaded operation condition that selects this overload
https://typemf.dev/generator type data type TypeScript type the data type maps to
https://typemf.dev/generator/feature get, set structural feature custom getter and setter body
https://typemf.dev/generator/import type, from, internal-from data type, operation, feature symbol to import and where from

Values are used as written, without checks. An empty value counts as missing.

documentation becomes the TSDoc comment of the generated element. If the https://typemf.dev/generator annotation has none, it is read from the source http://www.eclipse.org/emf/2002/Ecore. Documentation under the GenModel source http://www.eclipse.org/emf/2002/GenModel is not read.

body is the body of the generated method. If the https://typemf.dev/generator annotation has none, it is read from http://www.eclipse.org/emf/2002/GenModel. That is where EMF keeps Java bodies, so a .ecore file from EMF needs a TypeScript body under https://typemf.dev/generator for every such operation. An operation without a body throws when called.

Operations of the same name in a class become one TypeScript method with an overload signature each. The implementation runs the body of the overload whose condition matches the arguments. The generator derives the conditions from the parameters: a missing parameter must be undefined, and where the overloads differ in a parameter’s type, that type must be a primitive with a runtime test such as typeof x === 'string'. If it can’t derive a condition, set dispatch on the overload: a TypeScript condition over the parameter names. If no overload has a body, two overloads get the same condition, or a condition can’t be derived, the method throws when called.

get and set replace the generated getter and setter of a feature. The setter’s parameter is value.

  • A feature with only get has no stored value. The getter’s result is cached until the model changes. For a many-valued feature, the getter returns a read-only list.
  • A feature with get and set is not cached. If one of the bodies uses this._<feature name>, the class gets that field to store the value.
  • A many-valued feature has no setter, so set on it is ignored.
  • A custom setter replaces the generated notification logic too; sending notifications is up to the body.

A data type’s TypeScript type is, in this order: the type detail of https://typemf.dev/generator, the data type’s instanceClassName, or its name.

If that type has to be imported, add an annotation with source https://typemf.dev/generator/import:

Key Meaning
type The symbol to import. Without it, the annotation is ignored.
from The module other packages import it from.
internal-from Where the generated package itself imports it from, relative to its root when it starts with ./ or ../. Defaults to from.
<eClassifiers xsi:type="ecore:EDataType" name="Color">
<eAnnotations source="https://typemf.dev/generator">
<details key="type" value="Color"/>
</eAnnotations>
<eAnnotations source="https://typemf.dev/generator/import">
<details key="type" value="Color"/>
<details key="from" value="@example/colors"/>
</eAnnotations>
</eClassifiers>

The type-imports option of a config file overrides this annotation, for example for data types of a model you can’t change. The annotations of a data type are kept in the generated package, so generating another package that uses the data type reads them from there.

An operation or feature whose body uses other symbols gets one https://typemf.dev/generator/import annotation per symbol. Here internal-from applies, falling back to from.

<eOperations name="sortedBooks" upperBound="-1" eType="#//Book">
<eAnnotations source="https://typemf.dev/generator">
<details key="body" value="return sortByTitle(this.getBooks().toArray());"/>
</eAnnotations>
<eAnnotations source="https://typemf.dev/generator/import">
<details key="type" value="sortByTitle"/>
<details key="internal-from" value="../sorting.js"/>
</eAnnotations>
</eOperations>