12 обязательных типов Utility в TypeScript с использованием и примерами
Внедрение TypeScript и его возможностей статической типизации оказало глубокое влияние на опыт разработчиков в мире JavaScript, сделав его теперь отраслевым стандартом для большинства крупномасштабных баз кода.
Однако ценность TypeScript не ограничивается статической типизацией. Он демонстрирует свое мастерство посредством набора нескольких типов утилит — предопределенных конструкций, которые позволяют разработчикам плавно изменять и использовать существующие типы: отPartial
для создания дополнительных свойств доReturnType
для вывода результатов функции, оптимизации сложных манипуляций с типами, экономя разработчикам время и умственные способности.
В этой статье мы поговорим о 12 недооцененных типах утилит, которые вам следует чаще использовать в своей кодовой базе. Эти типы утилит TypeScript подобны инструментам в вашем наборе инструментов для кодирования, каждый из которых служит уникальной цели, помогая вам формировать свой код с точностью и ясностью.
Оглавление
Типы манипуляций с объектами
Partial
Required
Readonly
Mutable
Pick
Record
Omit
Типы манипуляций с объединением
Exclude
NonNullable
Extract
Типы функций
Parameters
Return Type
Awaited
Примечание об интерфейсах и типах
В этой статье мы будем использовать как интерфейсы, так и типы.
В TypeScript выбор между использованием типов и интерфейсов зависит от конкретного сценария. Интерфейсы подходят для описания формы объектов, определения их свойств, методов и событий. С другой стороны, типы более универсальны, позволяя определять псевдонимы примитивных типов, кортежи, объединения и работать с функциями, сложными типами и отображаемыми типами. В конечном итоге выбор между типами и интерфейсами зависит от конкретного варианта использования и решения разработчика.
1. Partial
Partial
позволяет создать тип, в котором все свойства типа T
станут необязательными. Это все равно что превратить строгий контрольный список «обязательно» в более расслабленный «выбирай, что хочешь».
Когда это полезно: при работе с формами или данными, в которых могут быть заполнены не все поля.
interface Pizza {
size: string;
toppings: string[];
delivery: boolean;
}
type PartialPizza = Partial<Pizza>;
const customPizza: PartialPizza = {
size: "Large",
toppings: ["Pepperoni", "Mushrooms"],
};
2. Required
Required
обеспечивает наличие всех свойств типа T
. Это все равно, что убедиться, что у вас есть все ключевые ингредиенты для вашего любимого блюда.
Когда это полезно: если вы хотите, чтобы определенные свойства всегда включались в ваши данные.
type RequiredPizza = Required<PartialPizza>;
const completePizza: RequiredPizza = {
size: "Medium",
toppings: ["Cheese", "Tomato"],
delivery: true,
};
3. Readonly
Readonly
создает тип, в котором все свойства типа T
доступны только для чтения. Думайте об этом как о политике «бесконтактного управления» для свойств вашего объекта.
Когда это полезно: Когда вы хотите предотвратить случайное изменение ваших данных.
type ReadonlyPizza = Readonly<Pizza>;
const classicPizza: ReadonlyPizza = {
size: "Small",
toppings: ["Olives", "Onions"],
delivery: false,
};
4. Pick
Pick
помогает извлечь тип, содержащий только указанные свойства, из типа T
. Думайте об этом как о создании собственной мини-версии объекта.
Когда это полезно: Когда вы хотите сузить свойства вашего объекта до самого необходимого.
type EssentialPizza = Pick<Pizza, 'size' | 'toppings'>;
const simplePizza: EssentialPizza = {
size: "Medium",
toppings: ["Tomatoes", "Cheese"],
};
5. Record
Record
создает тип с указанными ключами типа Keys
и значениями типа Value
. Представьте себе, что вы создаете свой собственный словарь слов и их значений.
Когда это полезно: Когда вам нужно создать структурированное сопоставление между ключами и значениями определенного типа.
type FruitDictionary = Record<string, string>;
const fruits: FruitDictionary = {
mango: "Yellow and delicious",
apple: "Red and round",
};
6. Omit
Omit
создает тип, исключая определенные свойства, перечисленные в разделе Keys
, из типа T
. Это похоже на редактирование ненужных частей изображения.
Когда это полезно: Когда вы хотите удалить определенные свойства из типа объекта.
type PizzaWithoutDelivery = Omit<Pizza, 'delivery'>;
const inPersonPizza: PizzaWithoutDelivery = {
size: "Large",
toppings: ["Bacon", "Jalapeno"],
};
7. Exclude
Exclude
фильтрует определенные типы из объединения, которые можно назначить исключенному типу. Это все равно, что сказать: «Мне нужен только один вид закусок».
Когда это полезно: Когда вы хотите сузить возможные значения типа объединения.
type SnackOptions = "Cookies" | "Fruit";
// Excluding "Cookies" for a guest with dietary restrictions
type GuestSnackOptions = Exclude<SnackOptions, "Cookies">;
const guest1: GuestSnackOptions = "Fruit"; // Valid
const guest2: GuestSnackOptions = "Cookies"; // Error: 'Chips' is excluded
8. NonNullable
NonNullable
гарантирует, что значение не является нулевым или неопределенным внутри объединения. Это ваш способ сказать: «Я хочу настоящего!»
Когда это полезно: когда вам нужно убедиться, что значение не отсутствует в типе объединения.
type NullableValue = string | null | undefined;
type SureValue = NonNullable<NullableValue>;
//same as type SureValue = string;
const certainValue: SureValue = "Absolutely!";
9. Extract
Extract
сохраняет только те типы из объединения, которые можно назначить извлеченному типу. Это все равно что вылавливать свои любимые блюда из набора закусок.
Когда это полезно: Когда вы хотите извлечь определенные типы из типа объединения.
type SweetFruit = Extract<string | number | boolean, string>;
//same as type SweetFruit = string;
const favoriteFruit: SweetFruit = "Mango"; // Delicious choice!
10. Parameters
Parameters
фиксируют типы параметров функции.
Когда это полезно: Когда нужно работать с типами параметров функции.
function cookDinner(mainCourse: string, sideDish: string): void {
console.log(`Tonight's menu: ${mainCourse} with a side of ${sideDish}`);
}
type DinnerIngredients = Parameters<typeof cookDinner>;
const tonight: DinnerIngredients = ["Spaghetti", "Salad"];
11. ReturnType
ReturnType
фиксирует тип возвращаемого значения функции. Это все равно, что знать, что ты получишь в качестве финального блюда.
Когда это полезно: Когда вы хотите знать, какой тип значения вернет функция.
function bakeCake(): string {
return "A delicious cake!";
}
type CakeType = ReturnType<typeof bakeCake>;
const cake: CakeType = "Chocolate"; // Yum, cake time!
12. Awaited
Awaited
извлекает разрешенный тип обещания, возвращаемого функцией. Это как получить настоящую еду после ожидания заказа.
Когда это полезно: Когда вы работаете с промисами и хотите знать, к чему они приведут.
async function fetchMeal(): Promise<string> {
const response = await fetch("https://api.example.com/meal");
return response.text();
}
type DeliveredMeal = Awaited<typeof fetchMeal>;
async function enjoyMeal(): Promise<void> {
const meal: DeliveredMeal = await fetchMeal();
console.log(`Enjoying a delicious meal: ${meal}`);
}
Бонус: Mutable
Mutable
создает тип, в котором все свойства типа T
являются изменяемыми. TypeScript не предлагает этот тип «из коробки», поэтому нам придется создать его самостоятельно.
Когда это полезно: когда у вас есть объект, доступный только для чтения, но вам нужно внести в него изменения.
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
type MutablePizza = Mutable<ReadonlyPizza>;
const dynamicPizza: MutablePizza = {
size: "Extra Large",
toppings: ["Jalapenos", "Tomatoes"],
delivery: true,
};
Пример комбинации
Давайте рассмотрим несколько примеров, интегрируя различные типы утилит в TypeScript. Мы возьмем тему создания команды супергероев с разнообразными способностями. Используя типы утилит, мы смоделируем различные аспекты этого сценария.
// Step 1: Define the types of superhero abilities
type Ability = "Flight" | "Strength" | "Invisibility" | "Telekinesis";
// Step 2: Create utility types for different types of superheroes
type SuperheroBase = {
name: string;
abilities: Ability[];
};
type FlyingSuperhero = Required<Pick<SuperheroBase, "name">> & {
abilities: ["Flight"];
};
type StrongSuperhero = Required<Pick<SuperheroBase, "name">> & {
abilities: ["Strength"];
};
type InvisibleSuperhero = Required<Pick<SuperheroBase, "name">> & {
abilities: ["Invisibility"];
};
type TelekineticSuperhero = Required<Pick<SuperheroBase, "name">> & {
abilities: ["Telekinesis"];
};
// Step 3: Create some superhero instances using the utility types
const superman: FlyingSuperhero = {
name: "Superman",
abilities: ["Flight"],
};
const hulk: StrongSuperhero = {
name: "Hulk",
abilities: ["Strength"],
};
const invisibleWoman: InvisibleSuperhero = {
name: "Invisible Woman",
abilities: ["Invisibility"],
};
const jeanGrey: TelekineticSuperhero = {
name: "Jean Grey",
abilities: ["Telekinesis"],
};
// Step 4: Create a superhero team with various abilities
type SuperheroTeam = FlyingSuperhero | StrongSuperhero | InvisibleSuperhero | TelekineticSuperhero;
const mySuperheroTeam: SuperheroTeam[] = [superman, hulk, invisibleWoman, jeanGrey];
// Step 5: Define an async action that our superheroes can perform
async function performHeroicAction(hero: SuperheroTeam): Promise<string> {
switch (hero.abilities[0]) {
case "Flight":
return "Rescued people from a tall building.";
case "Strength":
return "Lifted a fallen bridge to save civilians.";
case "Invisibility":
return "Sneaked past the enemy to gather information.";
case "Telekinesis":
return "Stopped a speeding car using telekinetic power.";
default:
return "Performed a mysterious heroic feat.";
}
}
// Step 6: Let our superheroes perform actions and communicate asynchronously
async function superheroTeamAssemble(team: SuperheroTeam[]): Promise<void> {
for (const hero of team) {
const actionResult: Awaited<ReturnType<typeof performHeroicAction>> = await performHeroicAction(hero);
const introduction = `${hero.name}: "${actionResult}"`;
console.log(introduction);
}
}
// Step 7: Assemble the superhero team and let them perform heroic actions
superheroTeamAssemble(mySuperheroTeam);
Важно отметить, что использованиеAwaited
иReturnType
здесь сделано явно, чтобы показать вам, как их можно использовать вместе. TypeScript обычно достаточно умен, чтобы распознавать тип разрешенного значения обещания и самостоятельно применять этот вывод типа.
В этом примере показано, как можно использовать служебные типы TypeScript для моделирования более сложных сценариев, включающих асинхронную связь, асинхронные действия и динамический анализ типов. Это также добавляет дополнительный уровень веселья и игривости нашему примеру с командой супергероев!
Заключение
Служебные типы TypeScript — это встроенные манипуляторы типов, которые облегчают изменение и выполнение действий над установленными типами. Надеюсь, вы нашли эти примеры типов утилит TypeScript информативными. Это не конец; вы можете использовать эти служебные типы для создания собственных типов. Использование служебных типов может значительно улучшить ваш код, сделав его более пригодным для повторного использования, кратким и чистым.
Для получения дополнительной информации о том, как вы можете использовать Cosmic в своем приложении с готовой поддержкой Typescript, посетите нашу документацию или начните работу с Cosmic.