The next thing we'll be taking a look at is TypeScript
.
This has been a bit of a divisive subject in Umbraco's history as the community have tried to get it into the core backoffice code base before, but given the nature of the codebase it was deemed too dificult. With a new codebase though, it really makes sense for it to be a first class citizen of the new UI and so it's really good to see it being adopted.
About TypeScript
In a nutshell TypeScript is JavaScript but strongly typed. What this means is that all variable/parameter types throughout your codebase will be explicitly defined and thus can be more easily checked for errors by developer tooling as you develop. This in turn reduces errors and makes for a more stable and predictable codebase.
TypeScript in and of itself though can't be run straight in the browser, it's really a middle layer to help with development. It must be compiled down to plain old JavaScript which can then be deployed within our application.
In our blog post series, this compilation is automatically taken care of for us by Vite.
The Basics
Explicit Types
Probably one of the most obvious things you'll notice when using TypeScript is how variables and parameters are often defined with an explicit type.
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
By providing these types our tooling can look for incorrect usages of our method and provide us with warnings that something is wrong.
greet("Matt", "2022-10-10");
// @err: Argument of type 'string' is not assignable to parameter of type 'Date'.
We are then able to rectify the mistake before it even becomes an issue in our running application.
greet("Matt", new Date("2022-10-10"));
If variables aren't defined with a type, TypeScript will attempt to infer the type from the assigned value and similalry warn if a future assignment is made that doesn't match that type.
let hello = "world";
hello = 42;
// @err: Type 'number' is not assignable to type 'string'.
Static Type Checking
What makes it possible for TypeScript to warn us about problems in our code is that by providing strongly types information about our data, it is able to build up an internal memory of these types and then pre-emtively via our tooling warn us of any misuse before we even run our application.
const message = "hello!";
message();
// @err: This expression is not callable. Type 'String' has no call signatures.
Without knowing these types, these checks could not be run and so the problems could only be found by running the application.
Non-exception Failures
Not only can we use TypeScript to validate our data types, we can also use it to look for code that is somewhat valid in JavaScript, but is often undesirable in practice, such as when accessing properties of an object that don't exist.
const user = {
name: "Daniel",
age: 26,
};
user.location;
// @err: Property 'location' does not exist on type '{ name: string; age: number; }'.
Here TypeScript can provide us with a warning, where in traditional JavaScript user.location
would simply return undefined
.
Auto-completion
Because TypeScript is able to understand our codebase, another nice feature it provides us is type auto-completion.
Advanced Topics
Whilst this post is meant as a basic overview of TypeScript, there is one advanced topic that is worth mentioning as it's a key concept used in the backoffic UI code.
Decorators
Decorators are a special syntax that allows us to encapsulate some behaviour to be applied to a class, method, property or parameter at runtime. These are often used as syntactic sugar to simplify repeative tasks.
Whilst I won't go into how to make our own decorators, it's worth seeing them in action so we know what they look like.
A very common decorator used in the backoffice UI is that for defining a web component and it's exposed properties.
@customElement('message-box')
export class MessageBoxElement extends LitElement {
@property({ type: string })
public kind: string = "info";
...
}
Whilst it is entirely possible as we saw in the Web Components post to register our component manually with the CustomElementRegistry
, the customElement
decorator enapsulates this behaviour for us and any class decorated with this expression will automatically be registered for us.
Similarly, we could expose our Web Components properties manually, with get
and set
functions that read and write the values to our elements attribtues
collection, but by using the property
decorator like this the implementation is taken care of for us.
NB: The customElement
and property
decorators used in this example are Lit specific decorators, but it's TypeScript that provides the mechanism for applying decorators.
Conclusion
TypeScript is very powerful and there is way more it can do than documented here, but what I've tried to do is just document the features you are likely to come across most day to day.
I'd highly recommend you take a look at the TypeScript docs for more on this subject, but I hope this gives you an intro to the subject.
One last thing to say on the subject is whilst you'll need to know TypeScript to be able to contribute to the new backoffice UI codebase, it is entirely optional when developing your own extensions so if you find it too much to deal with, then you don't need to use. Just note that most code examples you'll find from HQ and probably the community will use it so there will likely be some translation needed if you try to implement them in a codebase that isn't using TypeScript.
Personally I think it's a really worthwhile technology to get to grips with, especially for such a large application like Umbraco, but I think think it will also be really useful for package developers too.
Top comments (0)