The TypeScript team recently announced the TypeScript 2.2 release candidate which will contain key improvements to the TypeScript language. Most notably, are the introduction of the object type and improved support for mixins and composable classes.

Refinements: The object type and dotted property access

The object type fixes a previous limitation in defining a type definition where something can be an Object or a non-primitive type. This was not possible to handle with previous versions of TypeScript because number, string, and boolean could all be assigned to Object. So the new object type (note the lowercase) implies a type that is assignable to Object, except for primitives. See the examples in the TypeScript 2.2 release candidate announcement for more information.

Another key change was to allow property (dotted) access syntax for types with string index signatures. This change normalizes type checking behavior between foo.bar and foo['bar']. Previously, there were scenarios that did not work as expected with dotted access. The updates should now clean up the need for some unnecessary things.

This example:

contentStyles['transform'] = `...`;

could now be written as:

contentStyles.transform = `...`;

The potential downside with this change that will require further investigation relates to considering anything that has an index type and declared properties will not receive a compilation error if you typo a known property. We’ll keep you posted on our findings!

Our Favorite Update: Support for mixins and composable classes!

This change provides a significant improvement for our development of Dojo 2. Nearly two years ago we submitted a proposal for adding mixins and traits to TypeScript. At the time, the TypeScript team decided to not pursue the issue because it was expected that the TC39 working group would revisit the topic and add it to a future version of ES. In the interim, we created dojo/compose which solved this need reasonably well, but introduced challenges with very complex and difficult to type structures for complex objects.

And then, lo and behold, we saw the change on the TypeScript roadmap! We decided to quickly determine if TypeScript 2.2 would solve our needs for mixins and composition-based classes, and so far the results are promising.

Increased use of decorators

While not a new feature, with the change for mixins and composable classes, we’re increasingly using a feature of TypeScript that we had previously neglected: decorators.

With TS classes, we needed a way to replace the aspect after capabilities that we had with dojo/compose. Decorators are also useful for replacing core functionality that was previously achieved by convention. For example, to specify a custom property comparator no longer relies on a regular expression to identify the function, now a developer can just use the diffProperty decorator to register the custom function.

The result in moving from compose to TypeScript 2.2+ classes is a fairly significant reduction in the boilerplate needed to create the new class. Here are a pair of examples from an early proof of concept for dgrid 2:

export interface GridRowMixin extends WidgetMixin<GridRowProperties>, RegistryMixin { }

export type GridRow = Widget<GridRowProperties> & Themeable

export interface GridRowFactory extends WidgetFactory<GridRowMixin, GridRowProperties> { }

const createGridRow: GridRowFactory = createWidgetBase
	.mixin(registryMixin)
	.mixin(themeable)
	.mixin({
		mixin: {
			baseClasses: css,
			diffPropertyItem(this: GridRow, previousProperty: any, newProperty: any): PropertyChangeRecord {
				let changed;
				if (typeof newProperty.equals === 'function') {
					changed = !newProperty.equals(previousProperty);
				}
				else {
					changed = newProperty !== previousProperty;
				}

				return {
					changed,
					value: newProperty
				};
			},
			render(this: GridRow): DNode {
				// ...
			}
		}
	});

export default createGridRow;

And after:

export const GridRowBase = ThemeableMixin(RegistryMixin(WidgetBase));

@theme(css)
export default class GridRow extends GridRowBase<GridRowProperties> implements ThemeableMixinInterface {
	diffPropertyItem(previousProperty: any, newProperty: any): PropertyChangeRecord {
		let changed = newProperty !== previousProperty;

		if (typeof newProperty.equals === 'function') {
			changed = !newProperty.equals(previousProperty);
		}

		return { changed, value: newProperty };
	}

	render(): DNode {
		// ...
	}
}

The interfaces, types and structure for the class is simpler in the latter version with TypeScript 2.2 classes!

Giving early feedback

Rather than adding a new grammar to classes that might later conflict with the next version of ES, the TypeScript team achieved this result by removing some of the restrictions on classes. For example, it’s now possible to extend from a value that constructs an intersection type. They’ve also changed the way that signatures on intersection types get combined.

That said, it’s not been without challenges, which is why it’s been important to use important features early and give feedback as quickly as possible:

All of these issues had workarounds, and the latter two have already been addressed, so we remain confident that we can now leverage TypeScript 2.2 classes for most needs within Dojo 2, simplifying the developer ergonomics for our end users.

This change is one of the most significant improvements for making Dojo 2 easier to work with, and we’re very excited to quickly land a flurry of pull requests to update!