Які основні типи даних є в TypeScript?
TypeScript надає багатий набір типів даних, які допомагають створювати більш надійний та передбачуваний код. Розуміння цих типів - основа для ефективної роботи з TypeScript.
Type Annotations vs Type Inference
TypeScript може автоматично визначати типи (inference) або ви можете явно вказувати їх (annotations):
// Type inference - TypeScript сам визначає тип
let name = "Олександр"; // string
let age = 25; // number
// Type annotations - ми явно вказуємо тип
let name: string = "Олександр";
let age: number = 25;
Коли використовувати: Використовуйте type annotations коли TypeScript не може визначити тип автоматично або для покращення читабельності коду.
Примітивні типи
Boolean
Логічний тип з двома можливими значеннями:
let isDone: boolean = false;
let isActive: boolean = true;
let isLoading: boolean = false;
// Практичний приклад
function toggleMenu(isOpen: boolean): boolean {
return !isOpen;
}
// У функціональних компонентах
const [isVisible, setIsVisible] = useState<boolean>(false);
Best practice: Завжди ініціалізуйте boolean змінні явним значенням.
Number
Усі числа в TypeScript є числами з плаваючою точкою:
// Різні формати чисел
let decimal: number = 42;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let float: number = 3.14;
let negative: number = -10;
// Спеціальні числові значення
let infinity: number = Infinity;
let notANumber: number = NaN;
// Практичні приклади
function calculatePrice(price: number, tax: number): number {
return price + (price * tax / 100);
}
function isEven(num: number): boolean {
return num % 2 === 0;
}
Best practice: Використовуйте number для всіх числових значень. TypeScript не розрізняє цілі числа та числа з плаваючою точкою.
String
Текстові дані з підтримкою template literals:
let color: string = "синій";
let firstName: string = 'Олена';
// Template literals (рекомендовано)
let fullName: string = `${firstName} Коваленко`;
let age: number = 28;
let greeting: string = `Привіт! Мене звати ${fullName} і мені ${age} років.`;
// Багаторядкові рядки
let multiline: string = `
Це багаторядковий
рядок в TypeScript
`;
// Практичні приклади
function formatCurrency(amount: number, currency: string = "UAH"): string {
return `${amount} ${currency}`;
}
function createSlug(title: string): string {
return title.toLowerCase().replace(/\s+/g, '-');
}
Best practice: Використовуйте template literals для конкатенації рядків та багаторядкових текстів.
Array
Колекції елементів одного типу:
// Два способи оголошення масивів
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: Array<string> = ["apple", "banana", "orange"];
// Масиви об'єктів
interface User {
id: number;
name: string;
}
let users: User[] = [
{ id: 1, name: "Олег" },
{ id: 2, name: "Марія" }
];
// Практичні методи роботи з масивами
function getEvenNumbers(numbers: number[]): number[] {
return numbers.filter(num => num % 2 === 0);
}
function findUserById(users: User[], id: number): User | undefined {
return users.find(user => user.id === id);
}
// Readonly масиви
let readonlyNumbers: readonly number[] = [1, 2, 3];
// readonlyNumbers.push(4); // Помилка!
Best practice: Віддавайте перевагу синтаксису type[] над Array<type> для простіших типів.
Літеральні типи
Точні значення як типи:
// String literals
let direction: "left" | "right" | "up" | "down" = "left";
// Number literals
let diceRoll: 1 | 2 | 3 | 4 | 5 | 6 = 3;
// Boolean literals
let isTrue: true = true;
// Практичний приклад
type Theme = "light" | "dark" | "auto";
type Size = "small" | "medium" | "large";
interface ButtonProps {
theme: Theme;
size: Size;
disabled: boolean;
}
function createButton(props: ButtonProps): void {
// логіка створення кнопки
}
Спеціальні типи
Any
Відключає перевірку типів (використовуйте обережно):
let notSure: any = 4;
notSure = "може бути рядком";
notSure = false;
notSure.toUpperCase(); // Не буде помилки компіляції, але може бути runtime помилка
// Кращі альтернативи any
let value: unknown = getData(); // Безпечніше ніж any
let result: string | number = getValue(); // Union type замість any
Коли використовувати: Тільки при міграції з JavaScript або роботі з динамічним контентом. Намагайтеся уникати.
Unknown
Безпечна альтернатива any:
let userInput: unknown;
let userName: string;
userInput = 5;
userInput = "Max";
// userName = userInput; // Помилка!
// Потрібна перевірка типу
if (typeof userInput === "string") {
userName = userInput; // OK
}
Void
Відсутність значення (зазвичай для функцій):
function logMessage(message: string): void {
console.log(message);
// немає return або return без значення
}
function processData(data: any[]): void {
data.forEach(item => console.log(item));
}
// Змінні типу void (рідко використовується)
let unusable: void = undefined;
Null та Undefined
Представляють відсутність значення:
let u: undefined = undefined;
let n: null = null;
// З strictNullChecks
let name: string | null = null; // Може бути рядком або null
let age: number | undefined; // Може бути числом або undefined
// Перевірка на null/undefined
function getStringLength(str: string | null): number {
if (str !== null) {
return str.length;
}
return 0;
}
// Optional chaining
interface User {
name?: string;
address?: {
street?: string;
};
}
function getUserStreet(user: User): string | undefined {
return user.address?.street;
}
Never
Тип для значень, які ніколи не виникають:
// Функція, яка завжди кидає помилку
function error(message: string): never {
throw new Error(message);
}
// Функція з нескінченним циклом
function infiniteLoop(): never {
while (true) {
// нескінченний цикл
}
}
// Exhaustive checking
type Shape = "circle" | "square" | "triangle";
function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return Math.PI;
case "square":
return 4;
case "triangle":
return 3;
default:
const exhaustiveCheck: never = shape;
return exhaustiveCheck;
}
}
Об'єктні типи
Object
Загальний тип для об'єктів:
// Базовий object тип
let obj: object = { x: 0, y: 0 };
// Краще використовувати специфічні типи
interface Point {
x: number;
y: number;
}
let point: Point = { x: 10, y: 20 };
// Type aliases для об'єктів
type User = {
id: number;
name: string;
email?: string; // Optional property
readonly createdAt: Date; // Readonly property
};
// Index signatures
interface StringDictionary {
[key: string]: string;
}
let dict: StringDictionary = {
name: "Іван",
city: "Київ"
};
Функції
Типізація функцій різними способами:
// Function type
let myAdd: (x: number, y: number) => number;
myAdd = function(x: number, y: number): number {
return x + y;
};
// Arrow function
let multiply = (a: number, b: number): number => a * b;
// Optional parameters
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
// Default parameters
function createUser(name: string, role: string = "user"): User {
return { name, role };
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}
// Function overloads
function formatDate(date: Date): string;
function formatDate(date: string): string;
function formatDate(date: Date | string): string {
if (date instanceof Date) {
return date.toISOString();
}
return new Date(date).toISOString();
}
Користувацькі типи
Enum
Іменовані константи:
// Numeric enum
enum Direction {
Up = 1,
Down,
Left,
Right,
}
// String enum (рекомендовано)
enum Color {
Red = "red",
Green = "green",
Blue = "blue",
}
// Const enum (оптимізація)
const enum HttpStatus {
Ok = 200,
NotFound = 404,
InternalServerError = 500,
}
// Практичне використання
function setTheme(color: Color): void {
document.body.style.backgroundColor = color;
}
setTheme(Color.Blue);
Best practice: Використовуйте string enums для кращої читабельності та безпеки.
Tuple
Масиви з фіксованою кількістю та типами елементів:
// Базовий tuple
let coordinates: [number, number] = [10, 20];
let nameAge: [string, number] = ["Олена", 25];
// Labeled tuples
let point: [x: number, y: number] = [1, 2];
// Optional elements
let optionalTuple: [string, number?] = ["test"];
// Rest elements
let restTuple: [string, ...number[]] = ["prefix", 1, 2, 3];
// Практичні приклади
function useState<T>(initial: T): [T, (newValue: T) => void] {
let value = initial;
const setValue = (newValue: T) => {
value = newValue;
};
return [value, setValue];
}
// Деструктуризація
let [name, userAge] = nameAge;
let [x, y] = coordinates;
Union типи
Об'єднання декількох типів:
// Базові union типи
let id: number | string;
id = 101; // OK
id = "202"; // OK
// Union з literal types
type Status = "loading" | "success" | "error";
type Response = string | number | boolean;
// Discriminated unions
interface LoadingState {
status: "loading";
}
interface SuccessState {
status: "success";
data: string;
}
interface ErrorState {
status: "error";
error: string;
}
type AppState = LoadingState | SuccessState | ErrorState;
// Type guards
function handleState(state: AppState): void {
switch (state.status) {
case "loading":
console.log("Завантаження...");
break;
case "success":
console.log("Дані:", state.data);
break;
case "error":
console.log("Помилка:", state.error);
break;
}
}
// User-defined type guards
function isString(value: unknown): value is string {
return typeof value === "string";
}
function processValue(value: string | number): void {
if (isString(value)) {
console.log(value.toUpperCase()); // TypeScript знає, що це string
} else {
console.log(value.toFixed(2)); // TypeScript знає, що це number
}
}
Intersection типи
Об'єднання властивостей декількох типів:
interface Person {
name: string;
age: number;
}
interface Employee {
company: string;
position: string;
}
// Intersection type
type PersonEmployee = Person & Employee;
let worker: PersonEmployee = {
name: "Іван",
age: 30,
company: "TechCorp",
position: "Developer"
};
// Mixins pattern
interface Timestamped {
timestamp: Date;
}
interface Tagged {
tags: string[];
}
type BlogPost = {
title: string;
content: string;
} & Timestamped & Tagged;
Type Aliases vs Interfaces
// Type alias
type Point = {
x: number;
y: number;
};
// Interface
interface PointInterface {
x: number;
y: number;
}
// Розширення
type ExtendedPoint = Point & { z: number };
interface ExtendedPointInterface extends PointInterface {
z: number;
}
// Declaration merging (тільки для interfaces)
interface Window {
customProperty: string;
}
Коли використовувати:
- Interface: Для описання форми об'єктів, особливо коли потрібне розширення
- Type alias: Для union types, primitives, computed types
Практичні поради
1. Strict режим
// В tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
2. Utility types
// Partial - робить всі властивості опціональними
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>; // всі поля опціональні
// Pick - вибирає певні властивості
type UserPreview = Pick<User, "id" | "name">;
// Omit - виключає певні властивості
type CreateUser = Omit<User, "id">;
// Record - створює тип з ключами та значеннями
type UserRoles = Record<string, boolean>;
3. Conditional types
type IsArray<T> = T extends any[] ? true : false;
type Test1 = IsArray<string[]>; // true
type Test2 = IsArray<string>; // false
Висновок
Розуміння типів TypeScript дозволяє:
- ✅ Виловлювати помилки на етапі компіляції
- ✅ Покращити автодоповнення в IDE
- ✅ Зробити код більш читабельним та підтримуваним
- ✅ Забезпечити кращу документацію коду
Золоті правила:
- Використовуйте strict режим
- Уникайте
any, віддавайте перевагуunknown - Використовуйте union types замість
any - Застосовуйте type guards для звуження типів
- Використовуйте literal types для точності
Почніть з простих типів та поступово переходьте до більш складних конструкцій. TypeScript стане вашим надійним помічником у створенні якісного коду!