Studio Developer Manual / Version 2307
Table Of ContentsIn TypeScript, each class using the Ext Config system needs an additional interface that describes its Config options. The design goal for the representation of this Config interface is to only declare and document Config properties once, although they usually re-appear on the class itself. Also, we need to distinguish simple Configs and advanced ("bindable") Configs. Last but not least, Config objects usually only specify a subset of all possible properties.
Here, the TypeScript utility types Pick
and Partial
come in handy.
Pick
allows to pick a list of specified member declarations from another type.
Partial
creates a new type that is exactly like the source type, only that all members are
optional, as if they were declared with the ?
modifier.
All Config properties are declared in the class itself. "Simple" Config properties are just properties with
an optional default value, while bindable Config properties must be specified as an
accessor pair, typically encapsulating a private field. The additional Config type is
then declared as an interface using the partial type of picking those Config properties from the class.
By convention, we name this interface like the class, suffixed with Config
.
import Component from "@jangaroo/ext-ts/Component"; interface MyClassConfig extends Partial<Pick<MyClass, "configOption1" | "configOption2">> { } class MyClass extends Component { /** * Simple Config property. */ configOption1: string = "foo"; #configOption2: number[] = [42]; /** * Bindable Config property. */ get configOption2(): number[] { return this.#configOption2; } set configOption2(value: number[]) { this.#configOption2 = value; } constructor(config: MyClassConfig) { super(config); } } export default MyClass;
Example 5.8. Simple and Bindable Config Properties in TypeScript
To also export the additional interface, the most straightforward option seemed to be using a named export. But this has disadvantages:
When a class declares no additional Config properties, but just reuses the Config type of its superclass, it would have to re-export the super Config type.
When using both the class and its Config type, you need two import identifiers, which is especially cumbersome when there is a name clash, because you need to rename both.
So we decided to assign the Config type to the class, which can be done in TypeScript by declaring a
"virtual" class member, and use the name Config
for it.
interface MyClassConfig ... class MyClass ... { declare Config: MyClassConfig; ... }
Example 5.9. Declaring Config type as virtual class member
This allows to access the Config type by importing the class and then use the utility type called
Config
(imported from @jangaroo/runtime/Config
). As this pattern is followed
by all classes using the Ext Config System, also the Ext TS declarations of all framework components,
we can complement the
example by extending the superclass Config type. The pattern should also be used to refer to the Config
type for the constructor parameter.
import Config from "@jangaroo/runtime/Config"; import Component from "@jangaroo/ext-ts/Component"; interface MyClassConfig extends Config<Component>, Partial<Pick<MyClass, "configOption1" | "configOption2">> { } class MyClass extends Component { declare Config: MyClassConfig; //... constructor(config: Config<MyClass>) { super(config); } } export default MyClass;
Example 5.10. Extending superclass Config type