Irbisa · Шпаргалка
Система типов TypeScript
type и interface, any, unknown и never, сужение типов и type guards, структурная типизация, enum и литеральные union, дженерики и ограничения, keyof и typeof, mapped и conditional types, infer, .d.ts
В чём разница между
typeиinterfaceв TypeScript и что вы используете по умолчанию?interface User { id: string; name: string;} // declaration merging: a second interface adds fields…В чём разница между
any,unknownиnever?const loose: any = JSON.parse('{"name":"Ada"}');loose.name.toUpperCase(); // compilesloose.nmae.toUpperCase(); // compiles too, throws at runtime const data: unknown = JSON.parse('{"name":"Ada"}');// data.name; // error: 'data' is of type 'unknown'…Что происходит с типами TypeScript во время выполнения кода и почему нельзя написать
value instanceof User, еслиUserобъявлен как интерфейс?interface User { name: string;} class Account { id: string;…Что такое сужение типов (narrowing) и по каким проверкам TypeScript сужает тип?
function format(value: string | number | Date | null): string { if (value === null) return ''; // string | number | Date if (typeof value === 'string') return value.trim(); if (value instanceof Date) return value.toISOString(); return value.toFixed(2); // only number is left}…Что такое объединения (union) и пересечения (intersection) типов и что получится из
A & B, если оба объявляют одно свойство с разными типами?type Id = string | number; function show(id: Id): string { // id.toUpperCase(); // error: does not exist on type 'number' return typeof id === 'string' ? id.toUpperCase() : id.toFixed(0);}…TypeScript типизирован структурно: почему он принимает объект с лишним
labelв первом вызове и отклоняет во втором?interface Point { x: number; y: number;} function draw(p: Point) { console.log(p.x, p.y);} const p = { x: 1, y: 2, label: 'A' };draw(p); // okdraw({ x: 1, y: 2, label: 'A' }); // errorЧто такое type guard (
value is T) и функция-утверждение (asserts value is T) и чем рискованно их писать?interface User { id: string; name: string;} function isUser(value: unknown): value is User {…Почему многие команды избегают
enumв TypeScript и что используют вместо него?enum Direction { Up, Down,}console.log(Direction[0]); // 'Up': numeric enums map values back to namesconsole.log(Object.keys(Direction)); // ['0', '1', 'Up', 'Down']…Во что разворачивается каждый из этих псевдонимов типов и что здесь делают
keyof,typeofи индексный доступT[K]?const config = { api: 'https://example.com', retries: 3, features: ['search', 'chat'],};const roles = ['admin', 'editor'] as const; type Config = typeof config;type ConfigKey = keyof Config;type Retries = Config['retries'];type Feature = Config['features'][number];type Role = (typeof roles)[number];type Dict = keyof { [key: string]: boolean };Как написать дженерик
getProperty(obj, key), тип результата которого следует за ключом, и что означаетK extends keyof T?function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key];} const user = { name: 'Ada', age: 36, admin: false };…Что такое перегрузки функций в TypeScript и когда они лучше одной сигнатуры с параметром-объединением?
interface User { id: string; name: string;}declare function fetchUser(id: string): Promise<User>;…Что такое файл
.d.tsи как добавить типы для нетипизированного пакета или своё свойство вWindow?// src/types/globals.d.tsexport {}; // a module, so global additions go inside declare global declare global { interface Window { analytics?: { track(event: string, props?: Record<string, unknown>): void };…Что такое mapped type и как самому реализовать
Pick,OmitиDeepPartial?type MyPick<T, K extends keyof T> = { [P in K]: T[P];}; type MyOmit<T, K extends keyof T> = { [P in keyof T as P extends K ? never : P]: T[P];…Что такое условные типы и как
inferпозволяет написать свойReturnTypeили развернутьPromise?type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never; type UnwrapPromise<T> = T extends Promise<infer V> ? UnwrapPromise<V> : T; type ElementOf<T> = T extends readonly (infer E)[] ? E : never;…Что такое template literal types и как получить имена обработчиков вроде
onClickиз имени события вродеclick?type EventName = 'click' | 'focus' | 'change'; type HandlerName<E extends string> = `on${Capitalize<E>}`;type Handlers = HandlerName<EventName>; // 'onClick' | 'onFocus' | 'onChange' type HandlerProps<T> = {…Во что разворачиваются эти четыре псевдонима и почему квадратные скобки вокруг обеих сторон
extendsменяют результат?type ToArray<T> = T extends unknown ? T[] : never;type ToArrayWhole<T> = [T] extends [unknown] ? T[] : never; type A = ToArray<string | number>;type B = ToArrayWhole<string | number>;type C = ToArray<never>;type D = ToArrayWhole<never>;Как сделать два строковых id, например
UserIdиOrderId, несовместимыми в TypeScript (branded types)?declare const brand: unique symbol;type Brand<T, Name extends string> = T & { readonly [brand]: Name }; type UserId = Brand<string, 'UserId'>;type OrderId = Brand<string, 'OrderId'>;…Что такое ковариантность и контравариантность и почему TypeScript проверяет параметры методов мягче, чем свойства с типом функции?
interface Animal { name: string }interface Dog extends Animal { bark(): void }interface Cat extends Animal { meow(): void } type PropHandler = { handle: (animal: Animal) => void }; // property: checked strictlytype MethodHandler = { handle(animal: Animal): void }; // method: bivariant…