All skills
secondsky avatar

/sapui5

@620a19a
by Eddiesecondsky/sap-skills456 stars
120

This skill should be used when developing SAP UI5 applications, including creating freestyle apps, Fiori Elements apps, custom controls, testing, data binding, OData integration, routing, and troubleshooting. Use when building enterprise web applications with SAP UI5 framework, implementing MVC patterns, configuring manifest.json, creating XML views, writing controllers, setting up data models (JSON, OData v2/v4), implementing responsive UI with sap.m controls, building Fiori Elements apps, writing unit tests with QUnit, integration tests with OPA5, setting up mock servers, handling security (XSS, CSP), optimizing performance, implementing accessibility features, or debugging UI5 applications. Also covers sap.ui.mdc controls and TypeScript control libraries.

Use this Skill: https://skilld.dev/gh/secondsky/sap-skills/sapui5

This session only. Nothing lands on disk.

referencestypescript-support.md

≈3.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

TypeScript Support in SAPUI5

Source: Official SAP SAPUI5 Documentation Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First Last Updated: 2025-11-21


Overview

TypeScript enhances JavaScript development by adding type information, enabling early error detection and improved code assistance. SAPUI5 provides dedicated type definitions for fully-typed application development.

Benefits:

  • Early error detection during development
  • Better code completion and IntelliSense
  • Improved refactoring support
  • Enhanced documentation through types
  • Safer code through compile-time checks

Production Status: TypeScript support is production-ready and actively maintained by the SAPUI5 team.


Type Definition Packages

@sapui5/types (Recommended)

Officially maintained by the SAPUI5 development team:

npm install --save-dev @sapui5/types

Features:

  • Official type definitions
  • Regular updates with SAPUI5 releases
  • Full framework coverage
  • Maintained by SAP

Configuration (tsconfig.json):

{
    "compilerOptions": {
        "module": "es2022",
        "moduleResolution": "node",
        "target": "es2022",
        "lib": ["es2022", "dom"],
        "types": ["@sapui5/types"],
        "skipLibCheck": true,
        "strict": true
    }
}

@types/openui5 (Community)

Community-maintained via DefinitelyTyped:

npm install --save-dev @types/openui5

Use Case: Alternative for OpenUI5 projects or when specific versions needed.


Project Setup

Basic TypeScript SAPUI5 Project

package.json:

{
    "name": "my-sapui5-ts-app",
    "version": "1.0.0",
    "scripts": {
        "build": "tsc && ui5 build",
        "start": "ui5 serve",
        "watch": "tsc --watch"
    },
    "devDependencies": {
        "@sapui5/types": "^1.120.0",
        "@ui5/cli": "^3.0.0",
        "typescript": "^5.0.0"
    }
}

tsconfig.json:

{
    "compilerOptions": {
        "target": "es2022",
        "module": "es2022",
        "moduleResolution": "node",
        "lib": ["es2022", "dom"],
        "types": ["@sapui5/types"],
        "outDir": "./webapp",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "resolveJsonModule": true,
        "declaration": true,
        "sourceMap": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
}

ui5.yaml:

specVersion: "3.0"
metadata:
  name: my.sapui5.ts.app
type: application
framework:
  name: SAPUI5
  version: "1.120.0"
  libraries:
    - name: sap.m
    - name: sap.ui.core
    - name: sap.f

TypeScript Component

src/Component.ts:

import UIComponent from "sap/ui/core/UIComponent";
import models from "./model/models";
import Device from "sap/ui/Device";
import JSONModel from "sap/ui/model/json/JSONModel";

/**
 * @namespace my.sapui5.ts.app
 */
export default class Component extends UIComponent {
    public static metadata = {
        manifest: "json"
    };

    private contentDensityClass: string;

    public init(): void {
        // Call parent init
        super.init();

        // Set device model
        this.setModel(models.createDeviceModel(), "device");

        // Create router
        this.getRouter().initialize();
    }

    /**
     * Get content density CSS class
     */
    public getContentDensityClass(): string {
        if (this.contentDensityClass === undefined) {
            // Check if touch device
            if (!Device.support.touch) {
                this.contentDensityClass = "sapUiSizeCompact";
            } else {
                this.contentDensityClass = "sapUiSizeCozy";
            }
        }
        return this.contentDensityClass;
    }
}

TypeScript Controller

src/controller/Main.controller.ts:

import Controller from "sap/ui/core/mvc/Controller";
import MessageToast from "sap/m/MessageToast";
import MessageBox from "sap/m/MessageBox";
import JSONModel from "sap/ui/model/json/JSONModel";
import Filter from "sap/ui/model/Filter";
import FilterOperator from "sap/ui/model/FilterOperator";
import Event from "sap/ui/base/Event";
import ResourceBundle from "sap/base/i18n/ResourceBundle";
import ResourceModel from "sap/ui/model/resource/ResourceModel";
import Table from "sap/m/Table";
import SearchField from "sap/m/SearchField";

/**
 * @namespace my.sapui5.ts.app.controller
 */
export default class Main extends Controller {
    private viewModel: JSONModel;

    /*eslint-disable @typescript-eslint/no-empty-function*/
    public onInit(): void {
        // Create view model
        this.viewModel = new JSONModel({
            busy: false,
            selectedItemsCount: 0
        });
        this.getView()?.setModel(this.viewModel, "view");
    }

    /**
     * Search handler
     */
    public onSearch(event: Event): void {
        const searchField = event.getSource() as SearchField;
        const query = searchField.getValue();

        const filters: Filter[] = [];
        if (query && query.length > 0) {
            filters.push(new Filter("Name", FilterOperator.Contains, query));
        }

        const table = this.byId("productsTable") as Table;
        const binding = table.getBinding("items");
        binding?.filter(filters);
    }

    /**
     * Button press handler
     */
    public onPress(): void {
        const resourceBundle = this.getResourceBundle();
        MessageToast.show(resourceBundle.getText("buttonPressed") as string);
    }

    /**
     * Delete confirmation
     */
    public onDelete(): void {
        const resourceBundle = this.getResourceBundle();
        MessageBox.confirm(
            resourceBundle.getText("deleteConfirm") as string,
            {
                onClose: (action: string) => {
                    if (action === MessageBox.Action.OK) {
                        this.performDelete();
                    }
                }
            }
        );
    }

    /**
     * Get resource bundle
     */
    private getResourceBundle(): ResourceBundle {
        const model = this.getView()?.getModel("i18n") as ResourceModel;
        return model.getResourceBundle() as ResourceBundle;
    }

    /**
     * Perform delete operation
     */
    private performDelete(): void {
        // Delete logic here
        MessageToast.show("Item deleted");
    }
}

TypeScript Model/Formatter

src/model/formatter.ts:

import DateFormat from "sap/ui/core/format/DateFormat";
import NumberFormat from "sap/ui/core/format/NumberFormat";

export default {
    /**
     * Format date to localized string
     */
    formatDate(date: Date | string | null): string {
        if (!date) {
            return "";
        }

        const dateFormat = DateFormat.getDateInstance({
            pattern: "dd.MM.yyyy"
        });

        return dateFormat.format(new Date(date));
    },

    /**
     * Format currency
     */
    formatCurrency(amount: number | null, currency: string): string {
        if (amount === null || amount === undefined) {
            return "";
        }

        const currencyFormat = NumberFormat.getCurrencyInstance();
        return currencyFormat.format(amount, currency);
    },

    /**
     * Format status
     */
    formatStatus(status: string): string {
        const statusMap: Record<string, string> = {
            "A": "Approved",
            "R": "Rejected",
            "P": "Pending"
        };

        return statusMap[status] || status;
    },

    /**
     * Format status state
     */
    formatStatusState(status: string): string {
        const stateMap: Record<string, string> = {
            "A": "Success",
            "R": "Error",
            "P": "Warning"
        };

        return stateMap[status] || "None";
    }
};

Custom Control in TypeScript

src/control/CustomButton.ts:

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

/**
 * @namespace my.sapui5.ts.app.control
 */
export default class CustomButton extends Button {
    public static metadata = {
        properties: {
            customProperty: {
                type: "string",
                defaultValue: ""
            }
        },
        events: {
            customPress: {
                parameters: {
                    value: { type: "string" }
                }
            }
        }
    };

    // Property getter/setter
    public getCustomProperty(): string {
        return this.getProperty("customProperty") as string;
    }

    public setCustomProperty(value: string): this {
        this.setProperty("customProperty", value);
        return this;
    }

    // Custom renderer
    public static renderer = {
        ...Button.renderer,

        render: function(rm: RenderManager, control: CustomButton): void {
            // Custom rendering logic
            Button.renderer.render(rm, control);
        }
    };

    /**
     * Custom method
     */
    public customMethod(): void {
        this.fireEvent("customPress", {
            value: this.getCustomProperty()
        });
    }
}

Type Definitions for Custom Types

src/types/index.d.ts:

declare namespace my.sapui5.ts.app {
    // Custom types
    interface Product {
        id: string;
        name: string;
        price: number;
        currency: string;
        category: string;
        status: "A" | "R" | "P";
    }

    interface AppConfiguration {
        apiUrl: string;
        timeout: number;
        debug: boolean;
    }

    type StatusType = "A" | "R" | "P";
    type ViewMode = "display" | "edit" | "create";
}

Best Practices

1. Use Strict Mode

Enable strict TypeScript checking:

{
    "compilerOptions": {
        "strict": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "strictFunctionTypes": true
    }
}

2. Type Assertions

Use type assertions when type cannot be inferred:

const table = this.byId("productsTable") as Table;
const model = this.getView()?.getModel() as JSONModel;

3. Optional Chaining

Use optional chaining for nullable values:

const view = this.getView();
view?.setModel(model);

4. Generics with Models

Type your model data:

interface Product {
    id: string;
    name: string;
    price: number;
}

const model = new JSONModel<Product[]>([]);
const data = model.getData(); // Type: Product[]

5. Event Typing

Type event handlers properly:

import Event from "sap/ui/base/Event";
import Input from "sap/m/Input";

public onChange(event: Event): void {
    const input = event.getSource() as Input;
    const value = input.getValue();
}

Common Issues

Issue: Cannot find module 'sap/...'

Solution: Install type definitions:

npm install --save-dev @sapui5/types

Add to tsconfig.json:

{
    "compilerOptions": {
        "types": ["@sapui5/types"]
    }
}

Issue: Type errors with metadata

Solution: Use static metadata property:

export default class MyController extends Controller {
    public static metadata = {
        // metadata here
    };
}

Issue: 'this' implicitly has type 'any'

Solution: Use arrow functions or bind 'this':

// Arrow function
.then((data) => {
    this.processData(data);
});

// Bind
.then(function(data) {
    this.processData(data);
}.bind(this));

Compatibility

TypeScript Version: 4.0+ (5.0+ recommended) SAPUI5 Version: 1.60+ (1.120+ recommended) Node.js Version: 16+ (18+ recommended)


Migration from JavaScript

Step 1: Rename Files

Rename .js files to .ts:

mv Component.js Component.ts
mv controller/Main.controller.js controller/Main.controller.ts

Step 2: Add Type Annotations

Add types gradually:

// Before (JS)
onPress: function(oEvent) {
    var sValue = oEvent.getSource().getValue();
}

// After (TS)
public onPress(event: Event): void {
    const source = event.getSource() as Input;
    const value: string = source.getValue();
}

Step 3: Fix Type Errors

Address compilation errors one by one:

  • Add missing imports
  • Type function parameters
  • Use type assertions where needed
  • Enable strict mode gradually

Testing with TypeScript

QUnit Test (test/unit/model/formatter.ts):

import formatter from "my/sapui5/ts/app/model/formatter";

QUnit.module("Formatter Tests");

QUnit.test("Should format date correctly", (assert) => {
    const date = new Date(2025, 0, 21);
    const result = formatter.formatDate(date);
    assert.ok(result.includes("21"), "Date formatted correctly");
});

QUnit.test("Should format currency", (assert) => {
    const result = formatter.formatCurrency(100, "EUR");
    assert.ok(result.includes("100"), "Currency formatted");
});

Official Resources



Advanced TypeScript Topics

For advanced TypeScript patterns including:

  • Metadata-Driven Controls (MDC) with TypeScript
  • Control library development in TypeScript
  • Using @ui5/ts-interface-generator for control APIs
  • Enum registration and library.ts patterns

See references/mdc-typescript-advanced.md for detailed implementation guides.


Note: This document covers TypeScript support in SAPUI5. The framework actively maintains type definitions, making TypeScript a viable choice for new projects and migrations.

Source: SKILL.md on GitHub

No alerts1mo3 checks · Risk SAFE
  • Gen Agent Trust Hub1mo

    The sapui5 skill is a high-quality development assistant for building enterprise applications with the SAPUI5 framework. It provides extensive documentation, best practice checklists, and templates for components, controllers, and views. The skill also integrates with official SAP tools like the @ui5/mcp-server for advanced features like live code linting and project scaffolding. No security risks or malicious patterns were found; the skill even includes a dedicated guide on security best practices for developers.

  • Socket1mo

    No alerts

  • Snyk1mo

    Risk: LOW · No issues

Signed by skilld at 620a19a. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 months ago
Other metadata
metadata
{
  "maintainer": "Eduard Jiglau",
  "maintainer_email": "hello@sap-ai-skills.com",
  "website": "https://sap-ai-skills.com",
  "version": "2.4.1",
  "last_verified": "2026-05-31",
  "framework_version": "1.148.1 latest verified, 1.120.0+ baseline",
  "documentation_source": "https://github.com/SAP-docs/sapui5",
  "documentation_files_analyzed": 1416,
  "reference_files": 15,
  "mcp_integration": true,
  "mcp_tools": 9,
  "specialized_agents": 4,
  "slash_commands": 5,
  "status": "production",
  "keywords": [
    "sapui5",
    "ui5",
    "openui5",
    "sap ui5",
    "ui5 framework",
    "ui5 application",
    "ui5 development",
    "ui5 tooling",
    "ui5 cli",
    "Component.js",
    "manifest.json",
    "xml view",
    "data binding",
    "odata v2",
    "odata v4",
    "fiori elements",
    "sap.m controls",
    "mvc pattern",
    "routing",
    "navigation",
    "qunit",
    "opa5",
    "testing",
    "mock server",
    "sap.ui.mdc",
    "mdc table",
    "typescript",
    "accessibility",
    "security",
    "performance",
    "ui5 inspector"
  ]
}

README badge

README badge for secondsky/sap-skills/sapui5