Оптимизируйте свой код TypeScript с помощью утверждений const

В этой статье мы познакомимся с const assertions в TypeScript, с помощью которого мы можем опустить множество громоздких объявлений типов.

Обзор

В TypeScript 3.4 представлена ​​новая конструкция для литеральных значений, называемая константными утверждениями. Его синтаксис представляет собой утверждение типа с const вместо имени типа (например, 123 как const).

Когда мы создаем новые литеральные выражения с константными утверждениями, мы можем сигнализировать языку, где:

  • никакие литеральные типы в этом выражении не должны расширяться (например, нельзя переходить от «привет» к строке)
  • литералы объектов получают readonly свойства
  • литералы массива становятся readonly кортежами

Это введение из официальной документации. Давайте объясним их один за другим:

Нерасширяемые литеральные типы

Во-первых, давайте взглянем на литеральное расширение типа:

Когда мы используем ключевое слово let, мы видим, что переменная method имеет тип string. Мы можем изменить его значение позже, например, на POST :

// string
let method = 'GET';
// OK
method = 'POST';

А что, если заменить let на const?

Вы можете видеть, что его тип уменьшен до GET строкового литерала. Это также означает, что вы не можете назначить ему другие строки. Это также соответствует логике JavaScript, при использовании let мы, скорее всего, захотим позже изменить значение переменной, а const указывает, что мы не будем изменять значение переменной.

Литеральный тип точно описывает значение, например конкретную строку, число, логическое значение или член перечисления.

Затем нам может помочь утверждение const, которое генерирует нерасширенные литеральные типы. То есть он не расширяется до более общих типов, таких как следующий пример:

Как видите, он не расширен до типа string. Конечно, это всего лишь простой пример, в реальном кодировании, если ваша переменная method не изменится, вы должны использовать const для ее объявления.

Итак, что мы можем с этим сделать?

Если бы у нас была функция запроса, она выглядела бы так:

const request = (url: string, method: 'GET' | 'POST') => fetch(url, { method });

У нас также есть карта методов, когда мы используем ее следующим образом:

Он получает ошибку, потому что TypeScript считает тип METHOD_MAP таким:

Так почему же тип METHOD_MAP.GET не GET, а string? Мы явно используем const для объявления. Давайте посмотрим пример:

// { name: string; age: number; }
const person = {
  name: '1',
  age: 2,
};
// OK
person.age = 3;

Этот пример показывает, что TypeScript по умолчанию не делает за нас больше суждений, что гарантирует гибкость кода.

Итак, в настоящее время мы можем использовать утверждение const, чтобы сделать каждый тип значения свойства нерасширяемым литеральным типом. Как следующее:

Литерал объекта получает свойство только для чтения

Но добавление утверждения const к каждому свойству слишком избыточно, мы можем упростить это определение и использовать его непосредственно для литералов объектов:

Таким же образом мы добавляем дескриптор readonly. Разница между ними заключается в следующем:

За пределами файлов .tsx можно также использовать синтаксис утверждения угловых скобок.

const METHOD_MAP = <const>{
  GET: 'GET',
  POST: 'POST',
};
// Cannot assign to 'GET' because it is a read-only property
METHOD_MAP.GET = "GET"

Литерал массива становится кортежем только для чтения

Это легко понять:

Советы

В целом, утверждения const позволяют TypeScript принимать наиболее специфические типы выражений. Но мы также обращаем внимание на один важный момент при его использовании — оператор выражения.

Например, утверждения const могут применяться непосредственно только к простым литеральным выражениям:

// Error! A 'const' assertion can only be applied to a
// to a string, number, boolean, array, or object literal.
let a = (Math.random() < 0.5 ? 0 : 1) as const;
let b = (60 * 60 * 1000) as const;
// Works!
let c = Math.random() < 0.5 ? (0 as const) : (1 as const);
let d = 3_600_000 as const;

Существует также константный контекст, который не сразу преобразует выражение в полностью неизменяемое:

let arr = [1, 2, 3, 4];
let foo = {
  name: 'foo',
  contents: arr,
} as const;
// error!
foo.name = 'bar';
// error!
foo.contents = [];
foo.contents.push(5); // ...works!

Оба примера взяты из официальных предостережений, для получения более подробной информации вы можете ознакомиться с соответствующим запросом на включение.

Заключение

В упомянутом выше примере METHOD_MAP мы также можем сделать его enum. Как следующее:

Это связано с тем, что перечисления предназначены для описания именованных констант, поэтому их члены всегда доступны только для чтения. Таким образом, члены строковых перечислений имеют тип строкового литерала.

Затем некоторые разработчики не любят использовать enum. Потому что это недействительный JavaScript. Таким образом, утверждение const является допустимым решением в настоящее время.

Как следует из названия, утверждение const — это жемчужина, которая позволяет нам писать меньше объявлений типов. Если вы использовали Redux, утверждения const могут значительно снизить нагрузку на написание действий и редукторов и гарантировать строгость типов.

Один простой пример, показывающий, что делают утверждения const:

let test: {
  readonly a: 'a';
  readonly b: {
    readonly name: 1;
  };
  readonly c: readonly [true, false];
} = {
  a: 'a',
  b: { name: 1 },
  c: [true, false],
};

Спасибо, что прочитали. Если вам нравятся такие истории и вы хотите поддержать меня, пожалуйста, подумайте о том, чтобы стать участником Medium. Это стоит 5 долларов в месяц и дает вам неограниченный доступ к контенту Medium. Я получу небольшую комиссию, если вы зарегистрируетесь по моей ссылке.

Ваша поддержка очень важна для меня — спасибо.