Type Refinement (или уточнение типа) - это процесс, при котором TypeScript сужает тип значения, переходя от более общего к более конкретному, на основе проверок, условий или контекста.
Существует несколько основных способов уточнения типа.
🟣Уточнение через оператор typeof.
function printValue(value: string | number) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // string
} else {
console.log(value.toFixed(2)); // number
}
}Функция printValue принимает значение, которое может быть числом или строкой. Через проверку typeof в if блоке - мы сужаем с множества всех строк и чисел до множества строк. В ветке else, соответственно, тип автоматически станет числом.
🟣Уточнение через instanceof, когда мы работаем с классами.
class User {
constructor(public name: string) {}
}
function process(value: User | Date) {
if (value instanceof User) {
console.log(value.name);
} else {
console.log(value.toISOString());
}
}Если значение является экземпляром определенного класса, TypeScript корректно сузит тип внутри этого условия и поймет, что в каждой ветке будет инстанс определенного класса.
🟣Проверка структуры объекта через оператор in.
type TSuccess = { ok: true; data: string };
type TError = { ok: false; error: string };
function handle(res: TSuccess | TError) {
if ("data" in res) {
console.log(res.data); // TSuccess
} else {
console.log(res.error); // TError
}
}Здесь в примере у нас есть два типа, которые отличаются полями data и error. Проверив наличие поля data в объекте - тайпскрипт правильно определит с каким типом мы дальше будем работать.
🟣Использование дискриминирующих объединений.
type Circle = { type: "circle"; radius: number };
type Square = { type: "square"; size: number };
type Shape = Circle | Square;
function area(shape: Shape) {
switch (shape.type) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.size * shape.size;
}
}Это когда внутри объекта есть специальное поле, по значению которого можно однозначно определить, что это тот или иной тип.
TypeScript автоматически сопоставляет значения дискриминанта и сужает тип.
🟣Также существует механизм type assertion - утверждение типа.
const el = document.getElementById("div") as HTMLDivElement;Это не уточнение типа, а принудительное указание типа через оператор "as".
Использовать этот механизм нужно осторожно и редко, потому что он позволяет нарушить правила типизации.
При этом оператор "as" всё же проверяет пересечение типов, поэтому полностью некорректные значения передать туда не получится (скрин 1).
Конечно это можно обойти тем, что сначала привести тип к unknown, а потом уже к boolean, но как минимум это заставит разработчика задуматься, не делает ли он полную глупость.
✈️ Telegram | 🎓 Менторство | 📹YouTube | 👩💻 Roadmap
