5 первоклассных приемов TypeScript, которые вы ДОЛЖНЫ знать
TypeScript мощнее, чем просто интерфейсы
Когда я впервые столкнулся с TypeScript, должен признаться, я не был его фанатом. Разработчику JavaScript идея строго типизированного языка программирования казалась ненужной. Однако, погрузившись в TypeScript и изучив его расширенные функции и возможности, я понял, что этот язык может многое предложить.
Но этот мем суммирует мой опыт:
Работая с ним уже несколько лет, я могу с уверенностью сказать, что это то, где я хочу быть. Он мощный, безопасный и гибкий. Его система типов, интерфейсы и классы обеспечивают уровень структуры и надежности, которые могут значительно повысить удобство сопровождения вашего кода.
Но, как и в случае с любым новым инструментом, здесь есть кривая обучения. Я пытаюсь представить их в легко усваиваемом, применимом к реальному миру сценарии.
Типизированное возвращаемое значение в соответствии с параметром
type GetUsernameAdditionalArgs = { required?: boolean }; type GetUsernameReturnValue<T extends GetUsernameAdditionalArgs> = T["required"] extends true ? string : string | null; function getUsername<T extends GetUsernameAdditionalArgs>( id: number, additionalArgs?: T ): GetUsernameReturnValue<T> { // get username const username = "..."; if (additionalArgs?.required && username == null) { throw Error(""); } return username as GetUsernameReturnValue<T>; } // username will be of type string | null const username = getUsername(10); // requiredUsername will be of type string const requiredUsername = getUsername(10, { required: true });
Это очень полезный навык для применения аннотаций типов к функциям, которые могут возвращать значение null
или undefined
. Например, если у вас есть функция с именем getUsername
, которая извлекает данные из внешнего API, вы можете добавить блок if
для выдачи ошибки, если требуется имя пользователя.
Самое приятное то, что вы также можете отключить блок try-catch по мере необходимости, что дает вам больше контроля над поведением функции.
Расширьте интерфейс, чтобы сделать поля необязательными
interface User { username: string; userId: number; description?: string; } interface UserWithOptionalUsername extends Omit<User, 'username'> { username?: string; } const user: UserWithOptionalUsername = { userId: 10, };
Иногда нам может понадобиться сделать определенные поля необязательными в наших интерфейсах TypeScript. Для этого мы можем расширить тип утилиты Omit
из нашего базового интерфейса и переопределить поле со знаком вопроса (?
), чтобы сделать его необязательным.
Расширьте интерфейс, чтобы сделать поля обязательными
interface User { username: string; userId: number; description?: string; } interface UserWithDescription extends User { description: string; } const userWithDescription: UserWithDescription = { userId: 10, username: "", description: "", };
В некоторых сценариях нам может потребоваться сделать определенные свойства обязательными в наших интерфейсах TypeScript. Для этого мы можем расширить базовый интерфейс и переопределить свойство без знака вопроса (?
), чтобы сделать его обязательным.
Использование пространств имен в TypeScript
namespace MyService { export function findUser() {} } const user = MyService.findUser();
namespace
в TypeScript не используется широко, потому что мы можем просто использовать именованный импорт:
import * as Module from '.' console.log(Module.myVar);
Однако namespace
может обеспечить простой и строгий способ организации кода в логические группы, в отличие от module
.
Еще одно преимущество использования пространств имен заключается в том, что они помогают инкапсулировать код и предотвращают его утечку в глобальное пространство имен. Это может помочь предотвратить проблемы с загрязнением глобального пространства имен, что может привести к ошибкам и конфликтам между различными частями вашего кода.
Типизированный switch-case без значений по умолчанию
// initial code type Status = "running" | "pending" | "success"; function execByStatus(status: Status): number { switch (status) { case "running": return 1; case "pending": return 2; case "success": return 3; } } // =================================================================== // // modified Status type type Status = "running" | "pending" | "success" | "stuck"; // will give the error: Function lacks ending return statement and // return type does not include 'undefined' // error catched during compilation!! function execByStatus(status: Status): number { switch (status) { case "running": return 1; case "pending": return 2; case "success": return 3; } }
В JavaScript обычно используется регистр по умолчанию в операторах switch для обработки неожиданных изменений кода. Однако в TypeScript часто полезно вообще исключить регистр по умолчанию.
Не предоставляя вариант по умолчанию, мы можем обеспечить более строгую настройку оператора switch для конкретной обработки каждого отдельного случая. Если в будущем будут добавлены дополнительные случаи, компилятор TypeScript выйдет из строя во время компиляции, предупредив нас о новом случае и позволив нам правильно его обработать.
В целом, этот подход может помочь отловить ошибки и сделать наш код более надежным, поскольку мы можем быть уверены, что каждый случай обрабатывается явно и тщательно.
Дополнительно: требуется TypeScript, выбрать, пропустить и частично
Вот несколько сценариев, в которых вам может понадобиться реализовать некоторые из самых мощных типов в TypeScript:
Базовый интерфейс
// Base interface interface User { email: string; username: string; description: string; }
Требуется‹T›
// All properties in User is now required type UserWithDescription = Required<User>; const userWithDescription: UserWithDescription = { email: "", username: "", description: "", };
Required<T>
— это встроенный служебный тип в TypeScript, который создает новый тип, делая все свойства исходного типа T
обязательными.
Выберите‹Т›
// Only pick a few properties from base interface type UserWithoutDescriptionV1 = Pick<User, "email" | "username">; const userWithDescriptionV1: UserWithoutDescriptionV1 = { email: "", username: "", };
Pick<T, K>
— это встроенный служебный тип в TypeScript, который создает новый тип, выбирая набор свойств K
из исходного типа T
. Используйте |
, чтобы добавить больше ключей.
Пропустить‹T›
// Only pick a few properties from base interface type UserWithoutDescriptionV2 = Omit<User, "description">; const userWithDescriptionV2: UserWithoutDescriptionV2 = { email: "", username: "", };
Omit<T, K>
— это встроенный служебный тип в TypeScript, который создает новый тип, опуская набор свойств K
из исходного типа T
.
Частичное‹T›
type IncompleteUser = Partial<User>; const incompleteUser: IncompleteUser = {};
Partial<T>
— это встроенный служебный тип в TypeScript, который создает новый тип, делая все свойства исходного типа T
необязательными.
Эти трюки с TypeScript определенно полезны для ваших проектов. Дайте мне знать, если вы хотите, чтобы я написал о чем-нибудь еще, связанном с TypeScript. До скорого!