Введение
Решим реальную практическую задачу, с которой мне пришлось столкнуться на моем проекте React/TypeScript.
Задача
У нас есть массив конкретных строковых значений, таких как "first", "second", "third", "fourth" и "fifth". Необходимо отобразить их на странице, т.е как-то использовать, а также убедиться, чтобы эти данные были строго типизированы и TypeScript нам выдавал всплывающие подсказки при их использовании в коде.
Решение
Сначала поместим данные в неизменяемый (readonly) массив:
const numberNames = ["first", "second", "third", "fourth", "fifth"] as const
Мы объявили константу numberNames и присвоили ей массив строковых литералов;
Конструкция as const используется чтобы сделать из данного массива неизменяемый кортеж (tuple), где конкретные строковые литералы сохраняются как литеральные типы;
Сохранение строковых литералов в качестве литеральных типов означает, что компилятор TypeScript рассматривает каждый элемент в массиве как отдельный тип, соответствующий его точному строковому значению. Например, тип первого элемента 'first' не является просто общим строковым типом (string), а конкретно типом 'first';
Убедитесь в этом сами, создав два массива - один с as const, а другой без. А затем наведите на переменную и посмотрите разницу.
Теперь создадим тип на основе этого массива:
type NumberName = typeof numberNames[number]
Здесь мы создали тип NumberName, значением которого является объединение (union) типов строковых литералов из массива;
Индексатор числа [number] извлекает объединенный тип отдельных элементов массива. В результате typeof numberNames[number] представляет собой объединенный тип элементов массива:
"first" | "second" | "third" | "fourth" | "fifth"
Почему [number], а не, например [string]? Все просто - как мы знаем, к элементам массива можно получить доступ через числовой индекс: например,
numberNames[0] => "first"
;Даже если бы у нас был массив со значениями других типов, например [1, 'blabla', 555], то в любом случае мы бы использовали [number] для того, чтобы создать тип объединения на его основе
1 | 'blabla' | 555
;Кстати, данная возможность извлечения типа из кортежа при помощи индексатора [number], как у нас в примере, появилась в версии TypeScript 4.1.
Заключение
Для чего это все нужно - чтобы строго типизировать данный список строк и когда мы будем с ним работать, получать такого рода подсказки:
Я выбрал выбор типов (Type inference) и as const в качестве решения, потому что хотел реализовать его максимально гибким и с минимальным количеством кода.
P.S. Это мои первые шаги в области написания веб-статей, и это соответственно моя первая статья. Поэтому я хотел начать с чего-то небольшого, чтобы поделиться полезной информацией со всеми вами. Буду рад вашим комментариям. Спасибо!