Templates
Templates are a powerful tool that allow users to customize certain aspects of a type. Similar to generics in other programming languages, templates define template parameters that users can specify when referencing the type.
Templates can be applied to:
Default values
You can assign a default value to a template parameter using = <value>
.
Parameter constraints
You can impose constraints on template parameters using the extends
keyword. For details on how validation works, refer to the type relations documentation.
If you try to instantiate Foo with an argument that does not meet the string
constraint, you will encounter an error:
A template parameter constraint can also be a model expression:
Default values for template parameters must also adhere to the constraint:
Also, all optional arguments must be placed at the end of the template. A required argument cannot follow an optional argument:
Named template arguments
Template arguments can also be specified by name. This allows you to specify them out of order and omit optional arguments. This can be particularly useful when dealing with templates that have many arguments with defaults:
However, once a template argument is specified by name, all subsequent arguments must also be specified by name:
Since template arguments can be specified by name, the names of template parameters are part of the template’s public API. Renaming a template parameter may break existing specifications that use the template.
Note: Template arguments are evaluated in the order the parameters are defined in the template definition, not the order in which they are written in the template instance. While this is usually inconsequential, it may be important in some cases where evaluating a template argument may trigger decorators with side effects.
Templates with values
Templates can be declared to accept values using a valueof
constraint. This is useful for providing default values and parameters for decorators that take values.
When a passing a literal or an enum or union member reference directly as a template parameter that accepts either a type or a value, we pass the value. In particular, StringTypeOrValue
is a value with the string literal type "a"
.
The typeof
operator can be used to get the declared type of a value if needed.
Template parameter value types
When a template is instantiated with a value, the type of the value and the result of the typeof
operator is determined based on the argument rather than the template parameter constraint. This follows the same rules as const declaration type inference. In particular, inside the template TakesValue
, the type of StringValue
is the string literal type "b"
. If we passed a const
instead, the type of the value would be the const’s type. In the following example, the type of property
in M1
is "a" | "b"
.