New big release for TypeScript libraries

Hey Enonic-community,

I have just release new versions of our TypeScript libraries for XP: [email protected] and and [email protected]. :tada:

The good people at Enonic is currently working on “Typescript definitions in core”, but while we wait for that to be released, here are some new goodies in the mean time.

From Enonic Roadmap:

Highlights for xp-codegen-plugin release

Code is generated into a separate directory

The generated code will now be generated into its own directory ./.xp-codegen, and is of type *.d.ts. Then we don’t mix the developers code with the generated code anymore.

But if you do this trick in tsconfig.json, you can overlap the file structures of ./src/main/resources with ./.xp-codegen, and reference the types with very similar paths as before.

  "compilerOptions": {
+   "rootDirs": [
+     "./src/main/resources",
+     "./.xp-codegen"
+   ],
  "include": [
+   "./.xp-codegen/**/*",
  "exclude": ["./build/*"]

Generated types for content types can now be imported like this:

// The old way to import
-import type { Article } from "../../content-types/article/article";
-import type { Employee } from "../../content-types/employee/employee";

// The new way to import
+import type { Article, Employee } from "../../content-types";

// Or you can access globally defined type like this:
+type Article = XP.ContentTypes[""];

Generate global declarations

There are also some new global declarations that are automatically generated, which can be found at XP.ContentTypes, XP.XData and XP.SiteConfig.

Highlights in the enonic-types npm package release

Use global declarations

Instead of being passed as type parameters, enonic-types will now use global declarations for XP.ContentTypes, XP.XData and XP.SiteConfig.

This is more logical because XData and SiteConfig is per project, and not per content type. And this allows us to either specify them once, or generate the interfaces for them automatically.

They can be configured for a project like this:

import { MenuItem } from "../path-to-somewhere";

declare global {
  namespace XP {
    interface SiteConfig {
      footer: string | string[] | undefined;

    interface XData {
      "com-mysite"?: {
        "menu-item"?: MenuItem;

Content.type is now a “string literal” passed in as type parameter

By having the Content .type be a string literal that is passed in as a type parameter to Content, we can now split a discrete union of content types by a simple if-statement
on the type field (just like we would do in JavaScript).

import { query } from "/lib/xp/content";

export function get(): XP.Response {
  const res = query({
    count: 100,
    contentTypes: ["com.mysite:article", "com.mysite:employee"]
  // the shape of res.hits is correctly inferred based on `contentTypes`
  const contents: Array<Content<Article> | Content<Employee>> = res.hits;
  contents.forEach((content) => {
    // since we now know which shape of `data` belongs to which `type`, this is now possible
    if (content.type === "com.mysite:article") {
      // This would have failed earlier, since `content.type` as of type `string`
      const article: Article =;"Article: " + JSON.stringify(article))
    } else {
      // If it isn't an Article, it has to bee an Employee (based on `contentTypes` above)
      const employee: Employee =;"Employee: " + JSON.stringify(employee))

There will be a little work for users of xp-codegen-plugin to migrate to the new paths – sorry about that – but the result should be a bit cleaner code.

I hope you will like these new changes, there is some more info in the change logs:

Have a great day!
– Tom Arild


Here is a demo I created a before the summer showing how the shape of can be inferred from the contentTypesparameter in contentLib.query().

It also show how we can split a union of Content<A> | Content<B> by just doing a simple if-statement on Content.type.

Demo of new functionality

1 Like

Make sure to use the 2.0.1 version of xp-codegen-plugin.