Generated banner for Ui5 Typescript Conversion
Generated logo for Ui5 Typescript Conversion

Ui5 Typescript Conversion

ClineOfficialPublished by Cline

  • Skill
  • Guidelines
  • Software development
  • Apache-2.0
  • No issues found

From the author

A skill for converting UI5 (SAPUI5/OpenUI5) projects to TypeScript.
— the description by Cline

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured, step-by-step methodology for converting existing UI5 projects to TypeScript. It covers project configuration, code refactoring, and specific patterns for custom controls and test suites. The guidelines emphasize maintaining existing documentation, adopting modern ES module syntax, and leveraging type-safe interfaces to resolve runtime-generated method issues common in UI5 development.

What it does

  • Configures project files like package.json, tsconfig.json, and ui5.yaml for TypeScript support.
  • Provides patterns for converting proprietary UI5 class syntax to standard ES classes.
  • Guides the migration from legacy module definitions to modern ECMAScript modules.
  • Details the use of @ui5/ts-interface-generator to handle runtime-generated control methods.
  • Outlines specific architectural changes required for converting OPA integration tests.
  • Instructs on creating shared type definitions to improve project-wide type safety.

When to use it

  • When migrating a legacy SAPUI5 or OpenUI5 application to TypeScript.
  • When setting up a new UI5 project that requires strict type checking.
  • When converting custom UI5 controls to ensure they are compatible with TypeScript.
  • When refactoring existing QUnit and OPA tests to follow modern TypeScript patterns.

Advantages

  • Reduces runtime errors through static type checking.
  • Improves developer productivity with better IDE autocompletion and navigation.
  • Ensures compatibility with modern tooling and build processes.
  • Provides a clear, repeatable path for complex UI5 codebase migrations.

Usage examples

“How do I start converting my UI5 controller to TypeScript?”
The agent will guide you through changing the class definition to standard ES syntax, adding the @namespace JSDoc, and converting the sap.ui.define block to ES imports.
“I have a custom UI5 control that is failing type checks. How do I fix it?”
The agent will instruct you to install the @ui5/ts-interface-generator, run it to generate the .gen.d.ts files, and manually copy the required constructor signatures into your class.
“How should I convert my OPA5 tests to TypeScript?”
The agent will explain the shift from createPageObjects() to class-based Opa5 extensions and show how to remove Given/When/Then parameters from opaTest callbacks.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/cline/ui5-typescript-conversion/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip ui5-typescript-conversion.zip -d .agents/skills

Documentation by Cline

UI5 TypeScript Conversion Guidelines

This document outlines how a UI5 (SAPUI5/OpenUI5) project can be converted to TypeScript. It consists of the following parts:

  1. Important general rules

  2. How the setup of the project needs to be changed

  3. Converting the code itself

  4. Converting tests (reference to separate file)

General Conversion Rules

Preserve ALL comments

You MUST preserve existing JSDoc, documentation and comments - never remove JSDoc or comments during the conversion.

Example input:

/**
 * My cool controller, it does things.
 */
return Controller.extend("com.myorg.myapp.controller.BaseController", {
    /**
     * Convenience method for accessing the component of the controller's view.
     * @returns {sap.ui.core.Component} The component of the controller's view
     */
    getOwnerComponent: function () {
        // comment
        return Controller.prototype.getOwnerComponent.call(this);
    },
    ...
});

Wrong output:

export default class BaseController extends Controller {
    public getOwnerComponent(): UIComponent {
        return super.getOwnerComponent() as UIComponent;
    }
}

Correct output:

/**
 * My cool controller, it does things.
 * @namespace com.myorg.myapp.controller
 */
export default class BaseController extends Controller {
    /**
     * Convenience method for accessing the component of the controller's view.
     * @returns {sap.ui.core.Component} The component of the controller's view
     */
    public getOwnerComponent(): UIComponent {
        // comment
        return super.getOwnerComponent() as UIComponent;
    }
}
Be diligent

Carefully respect all guidelines in this document (and adapt appropriately where required). Before each conversion step, consider all relevant details from this document.

Go step-by-step

You should convert the project step by step, starting with the TypeScript project setup and then the most central files on which other files depend, so those other files can use the typed version of those central files once they are converted as well. "allowJs": true in the tsconfig.json's compilerOptions may be useful to run semi-converted projects if needed.

Avoid any type

Do not take shortcuts, but try to find the proper type or create an interface instead of any.

BAD:

(this.getOwnerComponent() as any).getContentDensityClass();

GOOD:

(this.getOwnerComponent() as AppComponent).getContentDensityClass()
Avoid unknown casts

Import and use actual UI5 control types instead (either the base class sap/ui/core/Control or more specific classes if needed to access the respective property). Inspect the XMLView to find out which control type you actually get when calling this.byId(...) in a controller! Don't forget using the specific event types like e.g. Route$PatternMatchedEvent for routing events.

Casting Example

BAD:

(this.byId("form") as unknown as {setVisible: (v: boolean) => void}).setVisible(false);

GOOD:

import SimpleForm from "sap/ui/layout/form/SimpleForm";
(this.byId("form") as SimpleForm).setVisible(false);
Create shared type definitions

Many type definitions you create are useful in different files. Create those in a central location like a file in src/types/.

Project Setup Conversion

1. package.json

You must add the following dev dependencies in the package.json file (very important) if they are not already present:

{{dependencies}}

However, if a dependency is already present in package.json, do not increase the major version number of it. Do not remove existing dependencies, you must only add new configuration. Install the dependencies early to verify the types are found.

IMPORTANT: In addition, you MUST also add the @sapui5/types (or @openui5/types) package in a version matching the UI5 project as dev dependency. Framework type and version can be found in ui5.yaml or using the get_project_info MCP tool.

In addition, if (and ONLY if) dependencies or their versions changed, ensure (or tell the user) to execute npm install / yarn install (whatever is used in the project) to get the changed dependencies in the project.

The typescript-eslint dependency is only relevant when the project already has an eslint setup (details are below).

Also add the "ts-typecheck": "tsc --noEmit" script to package.json, so you and the developer can easily check for TypeScript errors.

2. tsconfig.json

Add a tsconfig.json file. Use the following sample as reference, but adapt to the needs of the current project, e.g. adapt the paths map:

{
	"compilerOptions": {
		"target": "es2023",
		"module": "es2022",
		"moduleResolution": "node",
		"skipLibCheck": true,
		"allowJs": true,
		"strict": true,
		"strictNullChecks": false,
		"strictPropertyInitialization": false,
		"outDir": "./dist",
		"rootDir": "./webapp",
		"types": ["@sapui5/types", "@types/jquery", "@types/qunit"],
		"paths": {
			"com/myorg/myapp/*": ["./webapp/*"],
			"unit/*": ["./webapp/test/unit/*"],
			"integration/*": ["./webapp/test/integration/*"]
		}
	},
	"exclude": ["./webapp/test/e2e/**/*"],
	"include": ["./webapp/**/*"]
}
3. ui5.yaml

Update the ui5.yaml file to use the ui5-tooling-transpile-task and ui5-tooling-transpile-middleware and ensure that at least the following config is present:

builder:
  customTasks:
    - name: ui5-tooling-transpile-task
      afterTask: replaceVersion
server:
  customMiddleware:
    - name: ui5-tooling-transpile-middleware
      afterMiddleware: compression
    - name: ui5-middleware-livereload
      afterMiddleware: compression

Ensure that the generated ui5.yaml file is valid - avoid duplicate entries, each root configuration must only exist once. If a configuration like server already exists, you must add to it instead of adding a second entry.

4. Eslint configuration

Only when the project has eslint set up, enhance the eslint configuration with TypeScript-specific parts. If eslint is not set up with dependency in package.json and an eslint config, then do nothing. A complete eslint v9 compatible eslint.config.mjs file could e.g. look like this, but the actual content depends on the specific project, so you MUST adapt it!

import eslint from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";

export default tseslint.config(
	eslint.configs.recommended,
	...tseslint.configs.recommended,
	...tseslint.configs.recommendedTypeChecked,
	{
		languageOptions: {
			globals: {
				...globals.browser,
				sap: "readonly"
			},
			ecmaVersion: 2023,
			parserOptions: {
				project: true,
				tsconfigRootDir: import.meta.dirname
			}
		}
	},
	{
		ignores: ["eslint.config.mjs"]
	}
);

Application Code Conversion

Step 1: Change proprietary UI5 class syntax to standard ES class syntax

Every UI5 class definitions (SuperClass.extend(...)) must be converted to a standard JavaScript class. The properties in the UI5 class configuration object (second parameter of extend) become members of the standard JavaScript class. It is important to annotate the class with the namespace in a JSDoc comment, so the back transformation can re-add it. This @namespace comment MUST immediately precede the class declaration. The namespace is the part of the full package+class name (first parameter of extend) that precedes the class name.

Before (example):

[... other code, e.g. loading the dependencies "App", "Controller" etc. ...]
 
var App = Controller.extend("ui5tssampleapp.controller.App", {
    onInit: function _onInit() {
        // apply content density mode to root view
        this.getView().addStyleClass(this.getOwnerComponent().getContentDensityClass());
    }
});

After (example, do not use this code verbatim):

[... other code, e.g. loading the dependencies "App", "Controller" etc. ...]
 
/**
* @namespace ui5tssampleapp.controller
*/
class App extends Controller {
    public onInit() {
        // apply content density mode to root view
        this.getView().addStyleClass((this.getOwnerComponent()).getContentDensityClass());
    };
};
Step 2: Change to ECMAScript modules and imports

TypeScript UI5 apps must use modern ES modules and imports. Hence, convert all UI5 module definition and dependency loading calls (sap.ui.require(...), sap.ui.define(...)) to ES modules with imports (and in case of sap.ui.define a module export).

In the above example, this looks as follows.

Before:

sap.ui.define(["sap/ui/core/mvc/Controller"], function (Controller) {
    /**
     * @namespace ui5tssampleapp.controller
     */
    class App extends Controller {
        ... // as above
    };
 
  return App;
});

After:

import Controller from "sap/ui/core/mvc/Controller";
 
/**
* @namespace ui5tssampleapp.controller
*/
export default class App extends Controller {
    ... // as above
};

sap.ui.require shall be converted to just the imports and no export. Avoid name clashes for the imported modules.

Hint: importing sap/ui/core/Core does not provide the class (like for most other UI5 modules), but the singleton instance of the UI5 Core. So the imported module can be used directly for methods like byId(...) instead of calls to sap.ui.getCore() which return the singleton in JavaScript.

When sap.ui.require is used dynamically, e.g. sap.ui.require(["sap/m/MessageBox"], function(MessageBox) { ... }) inside a method body, then convert this to a dynamic import like import("sap/m/MessageBox").then((MessageBox) => { ... }).

Step 3: Standard TypeScript Code Adaptations

Apply your general knowledge about converting JavaScript code to TypeScript. In particular:

  • Add type information to method parameters and variables where needed.

  • Add missing private member class variables (with type information) to the beginning of the class definition. (In JavaScript they are often created later on-the-fly during the lifetime of a class instance.)

  • Convert conventional functions to arrow functions when someFunction.bind(...) is used because TypeScript does not seem to propagate the type of the bound "this" context into the function body.

  • Define further types and structures needed within the code, if applicable.

IMPORTANT: whenever you use a UI5 type, e.g. for annotating a variable or method parameter/returntype, do NOT use the UI5 type with its global namespace (like sap.m.Button or sap.ui.core.Popup)! Instead, import this UI5 type from the respective module (like sap/m/Button or sap/ui/core/Popup - add an import if needed) and use the imported module.

Example:

Wrong:

const b: sap.m.Button;
function getPopup(): sap.ui.core.Popup  { ... }

Correct:

import Button from "sap/m/Button";
import Popup from "sap/ui/core/Popup";
 
const b: Button;
function getPopup(): Popup  { ... }

Hint: use the actual UI5 control events, not browser events like Event or MouseEvent, in event handlers of UI5 controls. UI5 events are different. E.g. use the Button$PressEvent and Button$PressEventParameters from the sap/m/Button module when the press event of the sap/m/Button is handled.

Note: for any event XYZ of a UI5 control ABC, types like ABC$XYZEvent and ABC$XYZEventParameters are available!

Example:

Before:

sap.ui.define(["./BaseController"], function (BaseController) {
    return BaseController.extend("my.app.controller.Main", {
        onPress: function(oEvent) {
            const button = oEvent.getSource();
        },
        
        onSelectionChange: function(oEvent) {
            const items = oEvent.getParameter("selectedItems");
        }
    });
});

After:

import BaseController from "./BaseController";
import Button from "sap/m/Button";
import {Button$PressEvent} from "sap/m/Button";
import {Table$RowSelectionChangeEvent} from "sap/ui/table/Table";

export default class Main extends BaseController {
    onPress(oEvent: Button$PressEvent): void {
        const button = oEvent.getSource() as Button;
    }
    
    onRowSelectionChange(oEvent: Table$RowSelectionChangeEvent): void {
        const selectedContext = oEvent.getParameter("rowContext");
    }
}

Hint: use the most specific type which does provide all needed properties. Examples:

  • Use specific types like KeyboardEvent or MouseEvent, not just Event for browser events.

  • Use the Button$PressEvent from the sap/m/Button module, not the sap/ui/base/Event.

  • The same is valid for all types, not only events.

Step 4: Casts for Return Values of Generic Methods

Generic getter methods like document.getElementById(...) or someUI5Control.getModel() or inside a controller this.byId() return the super-type of all possible types (in the examples HTMLElement and sap.ui.model.Model and sap.ui.core.Element) although in practice it will usually be a specific sub-type (e.g. an HTMLAnchorElement or a sap.ui.model.odata.v4.ODataModel or a sap.m.Input).

In many cases you will have to cast the return value to the specific type to use it. The actual type can usually be derived from the context. If not, rather avoid the cast than guessing a wrong one. Also, do not cast to a superclass like sap.ui.model.Model when this is anyway the returned type.

The same is valid for several UI5 methods, most prominently the following:

  • core.byId() / view.byId()

  • control.getBinding()

  • ownerComponent.getModel()

  • event.getSource()

  • component.getRootControl()

  • this.getOwnerComponent()

This cast will sometimes also require an additional module import to make the type (like ODataModel above) known.

In the app controller example above, this step would add an additional import of the app's component (called AppComponent), so within the onInit implementation the required typecast can be done. Without this typecast, the return type of getOwnerComponent would be a sap.ui.core.Component, which does not have the getContentDensityClass method defined in the app component.

Before:

import Controller from "sap/ui/core/mvc/Controller";
 
/**
* @namespace ui5tssampleapp.controller
*/
export default class App extends Controller {
 
    public onInit() {
        // apply content density mode to root view
        this.getView().addStyleClass(this.getOwnerComponent().getContentDensityClass());
    };
 
};

After:

import Controller from "sap/ui/core/mvc/Controller";
import AppComponent from "../Component";
 
/**
* @namespace ui5tssampleapp.controller
*/
export default class App extends Controller {
 
    public onInit() : void {
        // apply content density mode to root view
        this.getView().addStyleClass((this.getOwnerComponent() as AppComponent).getContentDensityClass());
    };
 
};

(Note: the "void" definition of the method return type is not strictly demanded by TypeScript, but is beneficial e.g. depending on the linting settings.)

Step 5: Solving any Remaining Issues

At this point, the number of remaining TypeScript errors should be vastly reduced. If you clearly recognize some, fix them, but in case of doubt mention the last remaining issues to the developer.

UI5 Control TypeScript Conversion Guidelines

This section covers the conversion of UI5 custom controls from JavaScript to TypeScript. This applies both to single custom controls within applications and to control libraries.

Converting custom UI5 controls to TypeScript requires specific patterns in addition to the general TypeScript conversion (converting the proprietary UI5 class and syntax).

The Runtime-Generated Methods Problem (CRITICAL)

This is the most important aspect to understand.

UI5 generates getter/setter (and more) methods for all properties, aggregations, associations, and events at runtime. This means TypeScript cannot see these methods at development time, causing type errors.

The Problem

In a control with a text property defined in metadata:

static readonly metadata: MetadataOptions = {
    properties: {
        "text": "string"
    }
};

TypeScript will show errors when trying to use the generated methods:

rm.text(control.getText());  // ERROR: Property 'getText' does not exist on type 'MyControl'

Additionally, TypeScript doesn't know the constructor signature structure for initializing controls:

new MyControl("myId", {text: "Hello"}); // TypeScript doesn't know about the settings object structure

This affects:

  • Property getters/setters: getText(), setText(), bindText()

  • Aggregation methods: addItem(), removeItem(), getItems(), ...

  • Association methods: getLabel(), setLabel()

  • Event methods: attachPress(), detachPress(), firePress()

  • Constructor settings object structure

The Solution: @ui5/ts-interface-generator

Install the interface generator tool as a dev dependency:

npm install --save-dev @ui5/ts-interface-generator@{{ts-interface-generator-version}}

To make subsequent development easier, add a script like this to package.json:

{
    "scripts": {
        "watch:controls": "npx @ui5/ts-interface-generator --watch"
    }
}

NOTE: the tsconfig file related to the controls must be in the same directory in which the interface generator is launched. If you launch it in the root of your project and the tsconfig covering the TypeScript controls is in a subdirectory or has a different name than tsconfig.json, then call it like npx @ui5/ts-interface-generator --watch --config path/to/tsconfig.json.

After TypeScript conversion of all controls, run the generator once to generate the needed control interfaces:

npm run watch:controls

This generates a *.gen.d.ts file (e.g., MyControl.gen.d.ts) containing TypeScript interfaces with all the runtime-generated methods. TypeScript merges these interfaces with the control class.

These generated files should be committed to version control and never edited manually.

Required Constructor Signatures (CRITICAL MANUAL STEP)

After running the interface generator, you must manually copy the constructor signatures from the terminal output into the respective control class.

The generator outputs something like:

===== BEGIN =====
// The following three lines were generated and should remain as-is to make TypeScript aware of the constructor signatures 
constructor(id?: string | $MyControlSettings);
constructor(id?: string, settings?: $MyControlSettings);
constructor(id?: string, settings?: $MyControlSettings) { super(id, settings); }
===== END =====

Copy these lines into the beginning of the class body, before the metadata definition:

export default class MyControl extends Control {
    // The following three lines were generated and should remain as-is to make TypeScript aware of the constructor signatures 
    constructor(id?: string | $MyControlSettings);
    constructor(id?: string, settings?: $MyControlSettings);
    constructor(id?: string, settings?: $MyControlSettings) { super(id, settings); }

    static readonly metadata: MetadataOptions = {
        // ...
    };
}
Control Metadata Typing

The control metadata must be typed as MetadataOptions:

import type { MetadataOptions } from "sap/ui/core/Element";

export default class MyControl extends Control {
    static readonly metadata: MetadataOptions = {
        properties: {
            "text": "string"
        }
    };
}

Important points:

  • Import MetadataOptions from sap/ui/core/Element for controls (or closest base class - also available for sap/ui/core/Object, sap/ui/core/ManagedObject, and sap/ui/core/Component)

  • Use import type instead of import (design-time only, no runtime impact)

  • MetadataOptions available since UI5 1.110; use object for earlier versions

  • Typing prevents issues when inheriting from the control (inherited properties should not be repeated)

Namespace Annotation Required

The @namespace JSDoc annotation is required for the transformer to generate correct UI5 class names:

/**
 * @namespace ui5.typescript.helloworld.control
 */
export default class MyControl extends Control {
    // ...
}
Export Pattern

Must use export default immediately when defining the class, otherwise ts-interface-generator will fail:

// CORRECT:
export default class MyControl extends Control {
    // ...
}

// WRONG - separate export:
class MyControl extends Control {
    // ...
}
export default MyControl;
Static Members for Metadata and Renderer

Both metadata and renderer are defined as static class members:

import RenderManager from "sap/ui/core/RenderManager";

export default class MyControl extends Control {
    static readonly metadata: MetadataOptions = {
        properties: {
            "text": "string"
        }
    };

    static renderer = {
        apiVersion: 2,
        render: function (rm: RenderManager, control: MyControl): void {
            rm.openStart("div", control);
            rm.openEnd();
            rm.text(control.getText());
            rm.close("div");
        }
    };
}

The renderer can also be in a separate file (common in libraries) and should in this case stay separate when converting to TypeScript.

The following JavaScript code:

sap.ui.define([
    "sap/ui/core/Control",
    "./MyControlRenderer"
], function (Control, MyControlRenderer) {
    "use strict";

    return Control.extend("com.myorg.myapp.control.MyControl", {
        ...
        renderer: MyControlRenderer,
        ...

is then converted to this TypeScript code:

import Control from "sap/ui/core/Control";
import type { MetadataOptions } from "sap/ui/core/Element";
import MyControlRenderer from "./MyControlRenderer";

/**
 * @namespace com.myorg.myapp.control
 */
export default class MyControl extends Control {
    ...
    static renderer = MyControlRenderer;
    ...
Complete Control Example
JavaScript (Before):
sap.ui.define([
    "sap/ui/core/Control",
    "sap/ui/core/RenderManager"
], function (Control, RenderManager) {
    "use strict";
    
    var MyControl = Control.extend("ui5.typescript.helloworld.control.MyControl", {
        metadata: {
            properties: {
                "text": "string"
            },
            events: {
                "press": {}
            }
        },
        
        renderer: function (rm, control) {
            rm.openStart("div", control);
            rm.openEnd();
            rm.text(control.getText());
            rm.close("div");
        },
        
        onclick: function() {
            this.firePress();
        }
    });

    return MyControl;
});
TypeScript (After):
import Control from "sap/ui/core/Control";
import type { MetadataOptions } from "sap/ui/core/Element";
import RenderManager from "sap/ui/core/RenderManager";

/**
 * @namespace ui5.typescript.helloworld.control
 */
export default class MyControl extends Control {
    // The following three lines were generated and should remain as-is to make TypeScript aware of the constructor signatures 
    constructor(id?: string | $MyControlSettings);
    constructor(id?: string, settings?: $MyControlSettings);
    constructor(id?: string, settings?: $MyControlSettings) { super(id, settings); }

    static readonly metadata: MetadataOptions = {
        properties: {
            "text": "string"
        },
        events: {
            "press": {}
        }
    };

    static renderer = {
        apiVersion: 2,
        render: function (rm: RenderManager, control: MyControl): void {
            rm.openStart("div", control);
            rm.openEnd();
            rm.text(control.getText());
            rm.close("div");
        }
    };

    onclick(): void {
        this.firePress();
    }
}
Library-Specific Guidelines

When converting entire control libraries (not just single controls in apps), additional steps are required:

Library Module with Enums (CRITICAL to avoid XSS issues!)

In library.ts, enums must be attached to the global library object for UI5 runtime compatibility:

import ObjectPath from "sap/base/util/ObjectPath";

// Define enum as TypeScript enum
export enum ExampleColor {
    Red = "Red",
    Green = "Green",
    Blue = "Blue"
}

// CRITICAL: Attach to global library object
const thisLib = ObjectPath.get("com.myorg.myui5lib") as {[key: string]: unknown};
thisLib.ExampleColor = ExampleColor;

Why this is critical for every enum in the library:

  • Control properties reference types as global names: type: "com.myorg.myui5lib.ExampleColor"

  • UI5 runtime needs to find the enum via this global path

  • Without this, UI5 cannot validate the property type

  • This breaks type checking and can create XSS vulnerabilities as unchecked content can be written to HTML unexpectedly

Path Mapping in tsconfig.json

For libraries, add path mappings for the library namespace:

{
    "compilerOptions": {
        "paths": {
            "com/myorg/mylib/*": ["./src/*"]
        }
    }
}
Control Conversion Checklist

When converting a control from JavaScript to TypeScript:

  1. Convert to ES6 class/module like regular UI5 modules

  2. Add @namespace JSDoc annotation

  3. Use export default immediately with class definition

  4. Type metadata as MetadataOptions (import from appropriate base class)

  5. Define metadata and renderer as static members

  6. Install and run @ui5/ts-interface-generator

  7. Copy constructor signatures from generator output into class

  8. If in a library: manually attach enums to global library object

  9. Preserve all JSDoc comments and documentation

Test Conversion

There are critical, non-obvious patterns for converting UI5 test code from JavaScript to TypeScript. See the test conversion document for details when tests need to be converted..

Files

LICENSE

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to the Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by the Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding any notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   Copyright 2026 Cline Bot Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Cline
Publisher
Cline (Official)
Source
cline/skills · skills/ui5-typescript-conversion · commit 2637846
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Cline or Cline.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download ui5-typescript-conversion.zip