Customizing Generation

The output generated by tsgen can be customized either by using the TSModule Annotation and/or by command line Parameters. The latter is useful embedding data from the build process, e.g. the version number.

The most relevant customizations are done adding one TSModule Annotation at a package of your compilation unit.

Sometimes a configuration can be placed multiple times, e.g. as compiler argument and as a setting in the TSModule annotation. The precedence of settings for the effective configuration is:

  1. the default value in the TSModule annotation
  2. as parameter of a used TSModule annotation
  3. the arguments for the annotation processor

The TSModule Annotation

Currently only one TSModule annotation is permitted in one compilation unit. The annotation must be put to a package Element, like this:

@TSModule(
        moduleName = "namespace_test",
        nameSpaceMapping = "jts.modules.nsmap -> easy"
)
package jts.modules.nsmap;

import dz.jtsgen.annotations.TSModule;

The following annotation parameters are supported:

  • moduleName: The module name of the JavaScript/TypeScript Module. This must be a java package friendly name. This is a required parameter, if the TSModule annotation is used
  • additionalTypes: Array of full qualified Java type names, that should additionally be converted (since 0.4.0)
  • author: The author number for the package.json file
  • authorUrl: The authorURL for the package.json file
  • customTypeMappings: Custom Type Mapping for the module, the default is {}
  • description: the description for the package.json file
  • excludes: regular expression to exclude type conversion, default is: {"^sun", "^jdk.internal", "^java.lang.Comparable"}
  • getterPrefixes: The prefix filter for selecting the properties by getters. Default is: { "get([_a-zA-Z0-9].*)", "is([_a-zA-Z0-9]].*)"}
  • generateTypeGuards: Defines if typescript type gards should be generated as well. The default is false (since 0.3.0)
  • license: The license for the package.json file
  • nameMappingStrategy: The strategy for mapping getters / setters to member name: The default is NameMappingStrategy.JACKSON_DEFAULT (since 0.4.0)
  • nameSpaceMapping: The name space mapping, the default is {}
  • nameSpaceMappingStrategy: Defines how the default name space is calculated. Default is NameSpaceMappingStrategy.ALL_TO_ROOT (since 0.2.0)
  • outputType: The type of the output. Default is OutputType.NAMESPACE_AMBIENT_TYPE
  • setterPrefixes: prefix filter for members. Default is {"set([_a-zA-Z0-9].*)"} (sine 0.4.0)
  • version: The version number for the package.json file, default is “1.0.0”
  • enumExportStrategy: Defines how the default enum output strategy is. Default is EnumExportStrategy.NUMERIC

Note: The Processing Parameters tsgen may override some of these settings. See Processing Parameters for details.

Custom Type Mapping

The annotation processor supports a simple mapping description language. The custom Type Mapping for the module is a list of strings, each describing a type mapping. Each string consists of a Java Type (canonical name with type params) and the resulting TypeScript Type. Both Types are separated with an arrow, e.g. :

java.util.Date -> IDateJSStatic

maps a java.util.Date to the TypeScript type IDateJSStatic

It also is possible to use type variables, e.g. :

java.util.List<T> -> Array<T>

will convert any java.util.List or it’s subtypes to an Array type in TypeScript. If the matched java type has a type parameter the converted type parameter will be inserted accordingly.

Because in TypeScript the types Array and Map differ from [] or {} jtsgen is able to embed the type in a literal way. After the arrow the type variables can be expresed using the back tick character, e.g:

java.util.List<T> -> `T`[]

Limits: There are some constraints using those expressions: it is not possible to express the name spaces at the right hand side in a proper way. jtsgen adds a namespace to the java declaration types. Currently accessing this name space is out of scope.

Default Conversions

The following mappings can not be configured, for now:
  • The numerical primitive types are mapped to number
  • The primitive boolean is mapped to boolean
  • An array is mapped to `T`[]

The annotation processor has the following mapping for declaration types configured:

  • java.lang.Void -> Void
  • java.lang.Object -> Object
  • java.lang.String -> string
  • java.lang.Character -> string
  • java.lang.Number |-> number
  • java.lang.Boolean -> boolean
  • java.util.Collection<T> -> `T`[]
  • java.util.Map<U,V> -> { [key: `U`]: `V`; }

The processor has no knowledge about the the necessary imports.

Mapping-DSL

The Mapping DSL defined in ANTLR BNF variant:

mapping : origin  whsp* arrow whsp* target;
arrow : '->' | '|->'
origin :  jident  ( '.' , jident )*   tsAngleType?
target :  ( jident  '.' )*  tstypes+
tsLit :  tsChar*
tsAngleType : '<'  jident  ( ','  jident )* '>'
tsLitType : '`'  jident  '`'
tsTypes : tstype | ( tstype whtsp )*
tsType :   tsLit | tsangletype | tslittype | whtsp

jident :  ('a'-'z' | 'A' - 'Z' | '_' )  ('a'-'z' | 'A' - 'Z' | '_'  |  '0' - '9')*
tsChar :  * all chars expecpt '<' | '>' | '`' *

Name Space Mapping

TSModule accepts a list of name spaces, that should me mapped (shortened). That list will be prepended to the calculated name space mapping. The following name spave mapping strategies are available:

  • TOP_LEVEL_TO_ROOT: The top level java types are mapped to the root name space. Everything beneath is mapped into name spaces
  • ALL_TO_ROOT: All types are mapped to the root name space, only the types of same name reside in their own name space
  • MANUAL: No name space mapping is calculated

Some examples of :

  • a.b.c ->: Maps a.b.c (and beneath) to root
  • a.b.c -> a.b: Maps a.b.c to namespace a
  • =a.b.c ->: Maps only a.b.c to the root

Output: TypeScript Modules

The type of the output can be configured by the outputType parameter of the TSModule annotation:

  • NAMESPACE_AMBIENT_TYPE : exports a module with ambient types (d.ts and package.json) with a declared name space
  • NAMESPACE_FILE : only the ambient types with namespaces in a single d.ts file
  • MODULE : exports a declared module, e.g. using declare module at the top without ambient types
  • NO_MODULE : exports a single file containing all converted types without any surrounding namespace or module declaration (since 0.2.0)

Unfortunately the TypeScript team decided to disable access to files outside of the rootDir [TS-9858]. The strategey including the output of tsgen into your frontend project depends on the general project structure. Use one of the following options:

  1. Publish the generated module to the npmjs compatible repository (local or public). The disadvantage of this it that a an additional release step with a changed version number is needed for npm (or yarn) detecting a change
  2. Using the npm link feature
  3. No Module at all and instead copy the output directly into the TypeScript source directory.

Member Detection

tsgen detects the members of the converted type using the following rules:

  1. public non static members of a Java class
  2. existence of a getter method

By default tsgen adheres to the Java Beans specification [JavaBean], but this behavior can be modified. In the scope of this documentation the definition of getter and setter methods have to be extended to include members, that do not adhere the Java Beans specification, e.g. classes with Boolean properties generated by Kotlin. So:

  1. a setter method is any method that returns void, accepts exactly one argument. The method name matches the defined setter expression.
  2. a getter method is any method, that returns a type without any argument. The method name matches the defined getter expression.

To support the property naming conventions in Kotlin [CK] tsgen does not split getters to Boolean and non-Boolean types (isX, getX). For simplicity reasons only the following two options in TSModule define the getter and setter filter expressions:

  • getterPrefixes default: { "get([_a-zA-Z0-9].*)", "is([_a-zA-Z0-9]].*)" }
  • setterPrefixes default: { "set([_a-zA-Z0-9]].*)"}

Both prefixes act as an filter and as a function to extract the raw member of the member. The member name itself is defined by the member name mapping strategy (see next chapter). Only methods, that matches one of the both prefixes are considered as a getter or setter method. The group (that is the regular expression between the braces) extracts the the name which is applied to a name mapping function.

Member Name Mapping

The extracted raw member name has to match the one the used by the JSON framework. For example, in Jackson you define the mapping of the member names using a PropertyNamingStrategy [PNS]. tsgen tries to stick to the default setting of Jackson’s DataBind. If necessary you can change this name mapping in tsgen by setting the nameMappingStrategy in TSModule to one of the following strategy:

  • JACKSON_DEFAULT: the default Jackson property name mapping. This is the default used in tsgen
  • SIMPLE: no mapping at all
  • SNAKE_CASE: upper cases are interpreted as words that will be transformed to lower case words separated by underscores
  • UPPER_CAMEL_CASE: The first character is converted to upper case

The member name mapping strategy can be defined using the parameter nameMappingStrategy of the TSModule annotation.

Enum Output Strategy

  • NUMERIC: Writing numeric enums. This is the default used in tsgen
  • STRING: Writing string enums.