Вивчай
Домашнє завдання #18 ·
балівintermediate

Типізація функцій та змінних

Тобі дано набір JavaScript-функцій без типів. Твоє завдання -- переписати їх на TypeScript з повною типізацією: type annotations для параметрів і return, type aliases, union та literal types.


Завдання

1. Створення type aliases

Створи наступні типи:

// ID може бути рядком або числом
type ID = // ...

// Статус користувача -- один з трьох варіантів
type UserStatus = // "active" | ...

// Об'єкт користувача
type User = {
  id: // ID
  name: // string
  email: // string
  age: // number
  status: // UserStatus
}

2. Типізуй функцію processArray

// Оригінал (JS):
function processArray(arr, action) {
  if (action === "sum") {
    return arr.reduce((acc, n) => acc + n, 0);
  }
  if (action === "average") {
    return arr.reduce((acc, n) => acc + n, 0) / arr.length;
  }
  if (action === "max") {
    return Math.max(...arr);
  }
  if (action === "min") {
    return Math.min(...arr);
  }
  return null;
}

Вимоги:

  • arr -- масив чисел
  • action -- literal type з конкретними допустимими значеннями
  • Return type -- number | null

3. Типізуй функцію filterByType

// Оригінал (JS):
function filterByType(arr, typeName) {
  return arr.filter(item => typeof item === typeName);
}

Вимоги:

  • arr -- масив (string | number | boolean)[]
  • typeName -- literal type "string" | "number" | "boolean"
  • Return type -- (string | number | boolean)[]

4. Типізуй функцію calculateStats

// Оригінал (JS):
function calculateStats(numbers) {
  if (numbers.length === 0) {
    return { count: 0, sum: 0, average: 0, min: 0, max: 0 };
  }
  const sum = numbers.reduce((acc, n) => acc + n, 0);
  return {
    count: numbers.length,
    sum,
    average: sum / numbers.length,
    min: Math.min(...numbers),
    max: Math.max(...numbers),
  };
}

Вимоги:

  • Створи type alias Stats для об'єкта, що повертається
  • Параметр numbers -- масив чисел
  • Return type -- Stats

5. Типізуй функцію formatUser

// Оригінал (JS):
function formatUser(user, format) {
  if (format === "short") {
    return user.name;
  }
  if (format === "medium") {
    return `${user.name} (${user.email})`;
  }
  if (format === "full") {
    return `${user.name} (${user.email}), вік: ${user.age}, статус: ${user.status}`;
  }
  return user.name;
}

Вимоги:

  • user -- тип User (створений раніше)
  • format -- literal type
  • Return type -- string

6. Типізуй функцію parseConfig

// Оригінал (JS):
function parseConfig(raw) {
  const config = {
    host: "localhost",
    port: 3000,
    debug: false,
  };

  if (raw.host) config.host = raw.host;
  if (raw.port) config.port = raw.port;
  if (raw.debug !== undefined) config.debug = raw.debug;

  return config;
}

Вимоги:

  • Створи type alias Config для повного конфігу (всі поля обов'язкові)
  • Створи type alias PartialConfig для вхідного параметра (всі поля optional)
  • Return type -- Config

7. Типізуй функцію groupBy

// Оригінал (JS):
function groupBy(arr, key) {
  return arr.reduce((groups, item) => {
    const value = item[key];
    if (!groups[value]) {
      groups[value] = [];
    }
    groups[value].push(item);
    return groups;
  }, {});
}

Вимоги:

  • Параметр arr -- масив об'єктів User[]
  • Параметр key -- ключ User (використай keyof)
  • Return type -- об'єкт з масивами Record<string, User[]>

8. Типізуй функцію createValidator

// Оригінал (JS):
function createValidator(rules) {
  return function validate(value) {
    const errors = [];
    for (const rule of rules) {
      const error = rule(value);
      if (error) errors.push(error);
    }
    return errors;
  };
}

Вимоги:

  • Створи type alias ValidationRule -- функція (value: string) => string | null
  • rules -- масив ValidationRule[]
  • Return -- функція (value: string) => string[]

Приклад використання

// 1. Types
const user: User = {
  id: 1,
  name: "Олексій",
  email: "alex@test.com",
  age: 25,
  status: "active",
};

// 2. processArray
processArray([1, 2, 3, 4, 5], "sum");     // 15
processArray([10, 20, 30], "average");     // 20

// 4. calculateStats
const stats = calculateStats([10, 20, 30, 40, 50]);
// { count: 5, sum: 150, average: 30, min: 10, max: 50 }

// 5. formatUser
formatUser(user, "short");  // "Олексій"
formatUser(user, "full");   // "Олексій (alex@test.com), вік: 25, статус: active"

// 8. createValidator
const validateEmail = createValidator([
  (v) => (v.length === 0 ? "Email обов'язковий" : null),
  (v) => (!v.includes("@") ? "Email має містити @" : null),
]);

validateEmail("");           // ["Email обов'язковий", "Email має містити @"]
validateEmail("test");       // ["Email має містити @"]
validateEmail("test@ok.com"); // []

Бонус

  • Додай generic-версію processArray, яка працює не тільки з числами
  • Зроби groupBy generic-функцією, яка працює з будь-якими об'єктами (не тільки User)
  • Додай overloading для formatUser, де кожен формат повертає більш конкретний тип
  • Створи type guard функцію isUser(value: unknown): value is User для валідації об'єктів

Критерії оцінки

КритерійБали
Type aliases (ID, UserStatus, User, Stats, Config)15
processArray з literal type для action15
filterByType з union types10
calculateStats зі Stats type alias15
formatUser з literal type для format10
parseConfig з PartialConfig / Config10
groupBy з keyof15
createValidator з function type alias10
Бонус: generic groupBy / overloading / type guard+20