Trunc
Завдання
Реалізуйте типізовану версію Math.trunc
, яка приймає рядок або число та
повертає цілу частину числа шляхом видалення дробової частини. Наприклад:
type A = Trunc<12.34>; // 12
Розв’язок
Ми можемо легко отримати частину числа перед крапкою, якщо саме число буде рядком. В такому випадку, потрібно просто розділити рядок через крапку та взяти першу частину.
Завдяки рядковим тип-літералам у TypeScript це легко зробити. Отже, спочатку ми почнемо з порожнього типу, який нам потрібно реалізувати:
type Trunc<T> = any;
У нас є єдиний тип-параметр, який прийматиме саме число. Як ми вже говорили, буде легко отримати першу частину, розділивши рядок, тому нам потрібно перетворити число в рядок:
type Trunc<T> = `${T}`;
Отримуємо помилку: “Type ‘T’ is not assignable to type ‘string | number | bigint
| boolean | null | undefined”. Щоб виправити це, додамо обмеження над
тип-параметром T
:
type Trunc<T extends number | string> = `${T}`;
Тепер у нас є рядкове представлення нашого числа. Далі ми можемо використати умовний тип, щоб перевірити, чи рядок має крапку. Якщо так, ми виведемо його частини:
type Trunc<T extends number | string> = `${T}` extends `${infer R}.${infer _}`
? never
: never;
За допомогою цієї перевірки ми можемо розрізняти випадки, коли крапка існує, а коли її немає.
У випадку коли крапка існує, ми отримаємо фрагмент перед крапкою в тип-параметрі
R
і зможемо його повернути, ігноруючи частину після крапки:
type Trunc<T extends number | string> = `${T}` extends `${infer R}.${infer _}`
? R
: never;
Але що повертати, якщо крапки в рядку немає? Ну, це означає, що немає чого обрізати, тому ми повертаємо вхідний тип без змін:
type Trunc<T extends number | string> = `${T}` extends `${infer R}.${infer _}`
? R
: `${T}`;
Таким чином, ми проходимо всі тести на момент написання розв’язку!
Коментарі