Как типизировали запросы

Ответ

Для типизации запросов в TypeScript обычно используют fetch или библиотеки типа axios с дженериками. Пример с fetch:

interface User {
  id: number;
  name: string;
}

async function fetchUser(id: number): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  const data = await response.json();
  return data as User;
}

С axios:

import axios from 'axios';

axios.get<User>('/api/users/1').then(response => {
  const user = response.data; // user: User
});

Для сложных API используют zod или io-ts для валидации:

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string()
});

type User = z.infer<typeof UserSchema>;

const validateUser = (data: unknown) => UserSchema.parse(data);

Ответ 18+ 🔞

Ах ты ж ёпта, ну и тема подкинули — типизация запросов в TypeScript! Это ж святое, блядь, без этого сейчас как без рук, ага. Смотри, как мужики обычно делают, чтоб не облажаться с типами.

Вот, допустим, берёшь нативный fetch, простой как три копейки. Ты ему URL суёшь, он тебе ответ, а потом надо этот ответ как-то в нормальную форму привести, чтоб не прилетело левое говно. Смотри, как красота выглядит:

interface User {
  id: number;
  name: string;
}

async function fetchUser(id: number): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  const data = await response.json();
  return data as User;
}

Видишь? Интерфейс User описали — чётко, по-пацански. Потом функция, которая возвращает Promise<User>. И в конце этот as User — типа, "верь мне, компилятор, это точно юзер, я ж не мудак!". Хотя, конечно, если бэкенд накосячит и пришлёт тебе вместо имени массив из трёх хуёв, то тут тебе и as не поможет, пиздец настанет в рантайме.

А вот если ты не из тех, кто любит страдать хуйнёй, и предпочитаешь библиотеки — то axios тебе в руки, красава. Там вообще красота, можно дженерики прямо в вызове указать:

import axios from 'axios';

axios.get<User>('/api/users/1').then(response => {
  const user = response.data; // user: User
});

Смотри, какая магия: сказал get<User> — и всё, response.data уже умный, подсказывает поля id и name. Удобно, блядь, как швейцарские часы, только вот если сервер пришлёт какую-то дичь — опять обосрёшься на ровном месте.

И вот тут, сука, врываются ребята с тяжёлой артиллерией — валидаторы, ёб твою мать! Типа zod или io-ts. Это уже не просто "верь мне", а "проверяй, мудак, каждую косточку". Смотри, как zod выпендривается:

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string()
});

type User = z.infer<typeof UserSchema>;

const validateUser = (data: unknown) => UserSchema.parse(data);

Во, блядь! Создаёшь схему — UserSchema. Она описывает, что id должен быть числом, а name — строкой. Потом из этой схемы выводишь тип User — вообще гениально, ёпта! И функция validateUser принимает unknown (то есть "хз что, наверное, хуйня"), а возвращает уже провалидированного, честного User. Если данные не соответствуют — получишь ошибку, сразу ясно, где косяк. Это ж как мать родная: не даст тебе обосраться с неверными данными, прям в рот меня чих-пых, какая забота!

Короче, выбор за тобой: хочешь просто и быстро — fetch с as, хочешь удобнее — axios, а хочешь спать спокойно и не бояться, что бэкендер-олень тебе какую-то дичь подсунул — поднимай zod и валидируй, как последний параноик. Всё, вопрос закрыт, пошёл я дальше, дел овердохуища.