TypeScript / 39 минут
Массивы, кортежи и литералы
Различайте произвольный массив, кортеж, readonly и литеральные ограничения.
- Различайте произвольный массив, кортеж, readonly и литеральные ограничения.
- Применить тему в задаче «Массивы, кортежи и литералы» и объяснить граничные случаи.
Около 9 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.
Однородная последовательность и фиксированные позиции
Тип number[] описывает массив чисел произвольной допустимой длины. Тип [number, number] описывает кортеж из двух числовых позиций. Это полезно для координат, диапазона или результата с несколькими различающимися частями. Кортеж остаётся обычным JavaScript-массивом во время исполнения; его статический контракт не добавляет отдельного runtime-класса.
Выбирайте кортеж, когда смысл позиции действительно устойчив и короток. Для большого результата с пятью неочевидными строковыми позициями объект с именованными полями часто понятнее. Тип должен облегчать использование данных, а не заставлять читателя помнить, что означает индекс 3 в каждом вызове. Именованные элементы кортежа помогают документации, но не превращают позиции в реальные свойства объекта.
type Point = [x: number, y: number];
const point: Point = [10, 20];
const [x, y] = point;
console.log(x + y); // 30Union элементов не задаёт форму кортежа
Массив (string | number)[] может содержать эти типы в произвольном порядке и длине. Кортеж [string, number] требует строку в первой позиции и число во второй. Если функция ожидает такую пару, обычный массив союзного типа не доказывает соответствия. Компилятор не должен угадывать скрытое ограничение, которого нет в контракте.
type Entry = [string, number];
function show(entry: Entry): string {
return `${entry[0]}: ${entry[1].toFixed(0)}`;
}
show(['JS', 22]);При работе с внешним JSON кортеж тоже нужно проверить: массив ли это, подходящая ли длина и типы обеих позиций. Type assertion не является заменой такого разбора. Если массив может быть пустым, чтение элемента требует отдельного внимания; настройка noUncheckedIndexedAccess помогает не забыть возможный undefined у обычного индексного доступа.
readonly-массивы и параметры
readonly number[] или ReadonlyArray<number> сообщает, что через данную ссылку нельзя вызывать изменяющие операции и переприсваивать позиции. Это полезно для функции расчёта: она обещает не менять вход. Readonly-представление может принимать изменяемый массив для чтения, но не должно выдавать внешнему коду право менять данные, которые по контракту доступны только для чтения.
function sum(values: readonly number[]): number {
return values.reduce((total, value) => total + value, 0);
}
const values = [1, 2, 3];
console.log(sum(values)); // 6Ограничение поверхностное. Если элементы являются изменяемыми объектами, readonly внешнего массива не запрещает изменения их свойств автоматически. Кроме того, обычная другая ссылка на тот же runtime-массив может его менять. Это статическая дисциплина использования, а не физическое замораживание структуры.
Литеральные типы и as const
Литеральный тип описывает конкретное значение, например 'success', а не любую строку. Он полезен для дискриминантов и закрытых наборов. Вывод может расширять тип в зависимости от изменяемости и контекста. as const для литерального выражения помогает сохранить узкие значения и readonly-структуру вместо расширенного произвольного массива.
const directions = ['left', 'right'] as const;
type Direction = typeof directions[number];
const current: Direction = 'left';В этом примере typeof в позиции типа получает статический тип directions, а индекс number извлекает союз типов элементов. Это не тот же оператор, которым runtime-код проверяет typeof value === 'string'. Контекст записи определяет, о каком механизме идёт речь.
satisfies и сохранение полезного вывода
satisfies проверяет соответствие выражения типу, не превращая его в произвольное принудительное утверждение. Он удобен, когда хочется убедиться в форме конфигурации и при этом сохранить более точные свойства выведенного значения. Это проверка на этапе компиляции; JSON и другие внешние данные всё равно требуют runtime-валидации.
const limits = { js: 22, ts: 12 } satisfies Record<string, number>;
const point = [1, 2] satisfies [number, number];Не заменяйте все аннотации satisfies автоматически. Иногда потребителю нужен именно широкий публичный тип, а иногда — максимально точный внутренний вывод. Также не путайте satisfies и Object.freeze: первое не генерирует защиту от изменения объекта во время исполнения.
Практика координат
Функция принимает point и delta как пары чисел и возвращает новую пару сумм. Изменять исходные координаты нельзя. Благодаря кортежу известно, что обе позиции существуют, и возвращаемый тип должен содержать ровно две координаты. Возврат произвольного number[] может потерять это обещание, даже если конкретный пример визуально выглядит правильным.
Проверьте нулевое смещение, отрицательные координаты и взаимно противоположные изменения. Затем попробуйте сделать параметры readonly-кортежами и убедитесь, что чистая реализация продолжает работать. Для самостоятельного продолжения опишите диапазон времени и сравните удобство [start,end] с объектом {start,end}; выбирайте форму по тому, насколько ясно её будут читать потребители.