Skip to content

TypeScript ​

Chef supports TypeScript out of the box — .ts file compilation is built into the build pipeline. No additional setup is needed for building.

Initialization ​

For TypeScript to work in the IDE and for correct type checking, initialize the configuration:

bash
chef init build

The command creates three files in the project root:

FileDescription
aliases.tsconfig.jsonPath aliases for all extensions in the project
tsconfig.jsonMain TypeScript config, extends aliases
.browserslistrcTarget browsers for Babel and PostCSS

After initialization, TypeScript extensions are created automatically:

bash
chef create ui.buttons    # will create bundle.config.ts and src/ui.buttons.ts

Configuration ​

tsconfig.json ​

Generated with recommended settings:

json
{
  "extends": "./aliases.tsconfig.json",
  "compilerOptions": {
    "module": "ESNext",
    "target": "ESNext",
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "allowJs": true,
    "checkJs": false,
    "strict": true,
    "lib": ["ESNext", "DOM", "DOM.Iterable", "WebWorker"]
  },
  "include": [
    "**/*.ts"
  ]
}

If tsconfig.json already existed, Chef will not overwrite it. Add the line manually:

json
{
  "extends": "./aliases.tsconfig.json",
  // your settings...
}

aliases.tsconfig.json ​

Auto-generated based on all extensions in the project:

json
{
  "compilerOptions": {
    "types": ["./bitrix/js/ui/dev/src/ui.dev.ts"],
    "paths": {
      "main.core": ["./bitrix/js/main/core/src"],
      "ui.buttons": ["./local/js/ui/buttons/src"]
    }
  }
}

Paths in paths are resolved against the directory that contains aliases.tsconfig.json, so baseUrl is not needed — TypeScript 7 no longer supports it. If the file still has a baseUrl left by an earlier Chef version, regenerate the aliases.

This makes extension imports by name work both in the editor and during type checking:

ts
import { Loc, Tag } from 'main.core';
import { Button } from 'ui.buttons';

Updating aliases ​

When adding new extensions to the project, regenerate aliases:

bash
chef init build

The command rescans all extensions and updates aliases.tsconfig.json. tsconfig.json is not overwritten.

Bitrix API Types ​

The ui.dev extension provides types for the global BX object and other Bitrix APIs. Chef automatically includes it in aliases.tsconfig.json via the types field if the extension is found in the project.

After that, typed access is available in code:

ts
BX.message('hello');          // type string
BX.ready(() => { /* ... */ }); // callback

Writing Extensions ​

Entry Point ​

All public classes and functions are exported from the entry point:

ts
// src/ui.buttons.ts
export class Button {
  #node: HTMLElement;

  constructor(private options: { text: string }) {
    this.#node = document.createElement('button');
    this.#node.textContent = options.text;
  }

  render(): HTMLElement {
    return this.#node;
  }
}

export class ButtonGroup {
  #buttons: Button[] = [];

  add(button: Button): this {
    this.#buttons.push(button);
    return this;
  }
}

Importing Dependencies ​

Other extensions are imported by name — like npm packages:

ts
import { Tag, Loc } from 'main.core';
import { Button } from 'ui.buttons';
import { Popup } from 'main.popup';

During build, Chef replaces these imports with references to global dependency namespaces and writes the dependencies to config.php.

bundle.config.ts ​

The build config is also typed — import BundleConfig from @bitrix/chef:

ts
import type { BundleConfig } from '@bitrix/chef';

export default {
  input: './src/ui.buttons.ts',
  output: {
    js: './dist/ui.buttons.bundle.js',
    css: './dist/ui.buttons.bundle.css',
  },
  namespace: 'BX.UI.Buttons',
} as BundleConfig;

Tests in TypeScript ​

Unit Tests ​

ts
// tests/unit/ui.buttons.test.ts
import { it, describe } from 'mocha';
import { assert } from 'chai';

import { Button } from '../../src/ui.buttons';

describe('Button', () => {
  it('should render HTMLElement', () => {
    const button = new Button({ text: 'OK' });
    assert.instanceOf(button.render(), HTMLElement);
  });

  it('should contain correct text', () => {
    const button = new Button({ text: 'Save' });
    assert.equal(button.render().textContent, 'Save');
  });
});

For IDE type support, install locally:

bash
npm install --save-dev @types/mocha @types/chai

E2E Tests ​

ts
// tests/e2e/ui.buttons.spec.ts
import { test, expect } from '@playwright/test';

test('button renders on page', async ({ page }) => {
  await page.goto('/my-page');
  await expect(page.locator('.ui-btn')).toBeVisible();
});

For Playwright types in the IDE:

bash
npm install --save-dev @playwright/test

Type Checking ​

Chef checks types automatically during build (chef build). If there are type errors, the build will fail.

To check types without building, use the chef typecheck command:

bash
chef typecheck main.core                       # Check an extension
chef typecheck ui.bbcode.*                     # Check a group of extensions
chef typecheck main.core --file src/lib/dom.ts # Check a specific file
chef typecheck                                 # Check all in current directory

Type checking uses settings from tsconfig.json — including lib, strict and other compilerOptions. This ensures that errors in the IDE and during build are consistent.

Type checking is done by the TypeScript 7 compiler. If your IDE runs TypeScript 5.x, some errors may differ: TypeScript 7 infers types from the JS files of dependencies differently.

Some errors are not reported, because Bitrix globals are only partially typed:

  • Cannot find name (TS2304) — a reference to an undeclared global name;
  • Cannot use namespace 'BX' as a value (TS2708) — BX is used as a value while some extension declared only types in it;
  • an unresolved side-effect import (TS2882), such as import 'ui.forms' — unless noUncheckedSideEffectImports is enabled in tsconfig.json.

Released under the MIT License.