A Bitrix project with a 50,000-item catalog is a constant battle against type mismatches. When passing data from PHP to JavaScript, bugs often surface: undefined is not an object, wrong price types, broken filters. We regularly faced this on projects of any complexity. TypeScript solves this: static typing catches errors at compile time, not in the user's browser. Our team's experience — 8 years in Bitrix and over 50 projects with TypeScript architecture. Result: bugfix cost reduction of 30–50% and integration debugging time cut significantly. On one project, bugfix savings amounted to a substantial sum in the first year after migration.
Why TypeScript instead of vanilla JavaScript for Bitrix?
According to TypeScript Handbook (on Wikipedia), static typing prevents up to 15% of errors at compile time. In the Bitrix context this is critical: a wrong price type or product ID can break the cart. On a project with a 100,000-item catalog we completely eliminated filter bugs after switching to strict typing. TypeScript frontend is 2–3 times more reliable than a similar solution on plain JS: type errors, incorrect data transfer between PHP and JS, caching issues — all caught before production.
How is TypeScript integrated with PHP components?
We use a step-by-step approach to minimize downtime:
- Audit of current JS code: identify type mismatches, global variables, weak spots.
- Design types for all integration points with PHP (infoblocks, REST, components).
- Set up build tool (Vite or Webpack) with TypeScript support.
- Develop fully typed components — catalog, cart, filter.
- Test and deploy with stability guarantee.
TypeScript architecture in a Bitrix template
Two main approaches we apply:
| Approach | Description | When to choose |
|---|---|---|
| MPA with TypeScript modules | Classic PHP page generation, JavaScript is interactive islands. Each component is an isolated module with its own types. | Medium-sized projects where SEO and first-load speed are critical. |
| SPA/Headless | React or Vue on TypeScript fully control UI, Bitrix acts as API (REST/GraphQL). | Complex user interfaces: client cabinet, CRM widgets, multi-step forms. |
Most of our projects use the first approach with elements of the second in the most interactive parts (catalog, cart, personal account).
Example component structure
/local/templates/my_site/
├── src/
│ ├── components/
│ │ ├── catalog/
│ │ │ ├── CatalogFilter.ts
│ │ │ ├── ProductCard.ts
│ │ │ └── CartButton.ts
│ │ ├── cart/
│ │ │ ├── CartDrawer.ts
│ │ │ └── CartCounter.ts
│ │ └── common/
│ │ ├── Modal.ts
│ │ └── Tooltip.ts
│ ├── api/
│ │ ├── catalog.ts
│ │ ├── cart.ts
│ │ └── user.ts
│ ├── types/
│ │ ├── bitrix.d.ts
│ │ └── api.ts
│ ├── utils/
│ │ ├── http.ts
│ │ └── format.ts
│ └── main.ts
├── dist/
├── package.json
├── tsconfig.json
└── vite.config.ts
TypeScript cart component
Example of a typed add-to-cart component:
// api/cart.ts
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
img: string | null;
}
interface CartState {
items: CartItem[];
totalPrice: number;
totalCount: number;
currency: string;
}
interface AddToCartPayload {
productId: number;
quantity: number;
properties?: Record<string, string>;
}
export async function addToCart(payload: AddToCartPayload): Promise<CartState> {
const formData = new FormData();
formData.append('sessid', BX.bitrix_sessid());
formData.append('action', 'addItem');
formData.append('product_id', String(payload.productId));
formData.append('quantity', String(payload.quantity));
if (payload.properties) {
Object.entries(payload.properties).forEach(([k, v]) => {
formData.append(`props[${k}]`, v);
});
}
const res = await fetch('/local/ajax/cart.php', {
method: 'POST',
body: formData,
});
if (!res.ok) throw new Error(`Cart error: ${res.status}`);
const json = await res.json();
if (json.status !== 'success') throw new Error(json.error ?? 'Cart error');
return json.cart as CartState;
}
Integration with PHP components via data-attributes
Pass data from template to TypeScript without global variables:
// template.php of catalog component
<div
id="catalog-app"
data-section-id="<?= (int)$arResult['SECTION']['ID'] ?>"
data-iblock-id="<?= (int)$arParams['IBLOCK_ID'] ?>"
data-initial-filter='<?= htmlspecialchars(
json_encode($arResult['FILTER_PARAMS']), ENT_QUOTES
) ?>'
>
<?php // SSR markup for initial load ?>
</div>
// TypeScript reads data with types
const appEl = document.getElementById('catalog-app');
if (!appEl) throw new Error('#catalog-app not found');
const sectionId = Number(appEl.dataset['sectionId']);
const iblockId = Number(appEl.dataset['iblockId']);
const rawFilter = appEl.dataset['initialFilter'] ?? '{}';
const initFilter = JSON.parse(rawFilter) as FilterState;
How Vite speeds up TypeScript frontend build?
For multi-module projects we use Vite — it provides fast builds and code splitting.
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
root: 'src',
build: {
outDir: '../dist',
emptyOutDir: true,
rollupOptions: {
input: {
main: 'src/main.ts',
catalog: 'src/pages/catalog.ts',
cart: 'src/pages/cart.ts',
},
},
},
resolve: {
alias: { '@': '/src' },
},
});
Separate entrypoints for each section — only the needed JavaScript loads on the page, improving load speed.
Real case: catalog with 100,000 products
Our client — an e-commerce store with a 100,000-item catalog. Features: dynamic filters, cart with many options, integration with 1C via CommerceML. The original vanilla JS code suffered from type errors during 1C exchange. We performed an audit, designed types for all entities and migrated the frontend to TypeScript in three weeks. Result: cart errors decreased by 60%, filter load time by 30%. The client got a stability guarantee for the next two years, and bugfix savings amounted to a significant sum in the first year.
What is included in our TypeScript frontend development work
- Audit of current architecture: identify JavaScript issues, estimate migration volume.
- Set up TypeScript + build tool (Vite/Webpack) considering Bitrix environment.
- Create types for all integration points with PHP (infoblocks, REST, components).
- Develop new components (catalog, cart, filter, personal account) with full typing.
- Migrate existing code from vanilla JS to TypeScript preserving functionality.
- Architecture documentation and type description.
- Testing and debugging in production with stability guarantee.
Estimated timeline
| Stage | Duration |
|---|---|
| Audit and architecture design | 1–2 days |
| Set up TypeScript + Vite infrastructure | 1 day |
| Develop catalog components (filter, list, card) | 3–5 days |
| Develop cart and mini-cart in header | 2–3 days |
| Migrate existing JS code (depends on volume) | 2–5 days |
| Testing and deployment | 1–2 days |
Exact timeline and cost are calculated individually after getting to know your project. Order an audit of your project — we will analyze your code and offer the best solution. Get a consultation to understand how TypeScript will reduce your frontend maintenance costs.







