TypeScript — универсальный солдат современной разработки
Вы когда-нибудь мечтали написать одну кодовую базу и запустить её на сайте, в мобильном приложении и на десктопе? Лет десять назад это звучало бы как фантастика. Сегодня TypeScript даёт нам такую возможность без магии и жертв.
TypeScript — это не просто «JavaScript с типами». Это мощный инструмент, который превращает хаос динамической типизации в стройную архитектуру. А главное — он одинаково хорошо ложится на любую платформу. Веб, мобильные устройства (iOS, Android) и настольные системы (Windows, macOS, Linux) — везде есть свой TypeScript-фреймворк.
В этой статье мы разберём реальные стратегии и примеры кода, которые помогут вам стать настоящим кроссплатформенным разработчиком на TypeScript. Вы узнаете, как избежать дублирования логики, какие библиотеки выбрать и как не потерять производительность.
Почему TypeScript стал стандартом для кроссплатформенной разработки
Раньше для каждой платформы приходилось учить новый язык: Swift для iOS, Java/Kotlin для Android, C# для Windows, Python для бэкенда. TypeScript ломает этот стереотип. Он работает везде, где есть JavaScript-рантайм (браузер, Node.js, Deno) или нативная обёртка (React Native, Electron).
Основные преимущества TypeScript в кроссплатформенной разработке:
- Единая кодовая база — пишете бизнес-логику один раз, используете на всех платформах.
- Строгая типизация — отлавливаете баги на этапе компиляции, а не в рантайме.
- Богатая экосистема — тысячи библиотек с готовыми типами (DefinitelyTyped).
- Современный синтаксис — async/await, дженерики, декораторы, утилитарные типы.
Кстати, если вы уже работаете с Python и хотите попробовать TypeScript, то вас ждёт приятный сюрприз: многие концепции (статическая типизация, интерфейсы, дженерики) очень похожи. На pythonlib.ru мы любим показывать, как разные языки решают одни и те же задачи. TypeScript в этом смысле — идеальный мост между вебом и нативом.
Архитектура кроссплатформенного проекта на TypeScript
Чтобы код работал везде, нужно разделить приложение на три слоя:
- Ядро (Core) — чистая бизнес-логика, не зависящая от платформы.
- Адаптеры (Adapters) — тонкие прослойки для UI, файловой системы, сети.
- Платформенные обёртки — React для веба, React Native для мобилок, Electron для десктопа.
Рассмотрим пример: приложение для заметок с тегами. Ядро будет содержать только типы и функции работы с данными.
// core/types.ts
export interface Note {
id: string;
title: string;
content: string;
tags: string[];
createdAt: Date;
}
// core/noteService.ts
export function filterNotesByTag(notes: Note[], tag: string): Note[] {
return notes.filter(note => note.tags.includes(tag));
}
export function createNote(title: string, content: string, tags: string[]): Note {
return {
id: crypto.randomUUID(),
title,
content,
tags,
createdAt: new Date(),
};
}
Этот код можно использовать в любом окружении. Никаких зависимостей от DOM, React или Node.js. Чистая TypeScript-логика.
Кроссплатформенный UI: React + React Native + Electron
Самый популярный стек для кроссплатформенной разработки на TypeScript — это React (веб), React Native (мобильные) и Electron (десктоп). Все три используют один синтаксис компонентов, но с разными рендерерами.
Шаг 1. Создаём общие компоненты
Можно вынести логику отображения в абстрактные компоненты, которые будут использовать платформенные примитивы.
// shared/NoteCard.tsx
import { View, Text, StyleSheet } from 'react-native'; // для React Native
// import { View, Text, StyleSheet } from 'react-native-web'; // для веба
interface NoteCardProps {
title: string;
preview: string;
onPress: () => void;
}
export const NoteCard: React.FC<NoteCardProps> = ({ title, preview, onPress }) => {
return (
<View style={styles.card} onClick={onPress}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.preview}>{preview}</Text>
</View>
);
};
const styles = StyleSheet.create({
card: {
padding: 16,
margin: 8,
backgroundColor: '#fff',
borderRadius: 8,
shadowColor: '#000',
shadowOpacity: 0.1,
shadowRadius: 4,
},
title: {
fontSize: 18,
fontWeight: 'bold',
},
preview: {
fontSize: 14,
color: '#666',
},
});
Обратите внимание: onClick в вебе — это onPress в React Native. Но TypeScript позволяет использовать условные типы, чтобы сделать API единым.
Шаг 2. Платформенные адаптеры
Для работы с хранилищем данных создадим интерфейс и две реализации.
// core/storage.ts
export interface IStorage {
getNotes(): Promise<Note[]>;
saveNotes(notes: Note[]): Promise<void>;
}
// web/storage.ts (LocalStorage)
export class WebStorage implements IStorage {
async getNotes(): Promise<Note[]> {
const data = localStorage.getItem('notes');
return data ? JSON.parse(data) : [];
}
async saveNotes(notes: Note[]): Promise<void> {
localStorage.setItem('notes', JSON.stringify(notes));
}
}
// native/storage.ts (AsyncStorage для React Native)
import AsyncStorage from '@react-native-async-storage/async-storage';
export class NativeStorage implements IStorage {
async getNotes(): Promise<Note[]> {
const data = await AsyncStorage.getItem('notes');
return data ? JSON.parse(data) : [];
}
async saveNotes(notes: Note[]): Promise<void> {
await AsyncStorage.setItem('notes', JSON.stringify(notes));
}
}
// desktop/storage.ts (fs для Electron)
import { app } from 'electron';
import * as fs from 'fs';
import * as path from 'path';
export class DesktopStorage implements IStorage {
private filePath = path.join(app.getPath('userData'), 'notes.json');
async getNotes(): Promise<Note[]> {
if (!fs.existsSync(this.filePath)) return [];
const data = fs.readFileSync(this.filePath, 'utf-8');
return JSON.parse(data);
}
async saveNotes(notes: Note[]): Promise<void> {
fs.writeFileSync(this.filePath, JSON.stringify(notes), 'utf-8');
}
}
Теперь в главном файле приложения мы просто подставляем нужную реализацию в зависимости от платформы:
// app.ts
import { WebStorage }