Skip to content
Back to skills

Typescript

ASecurity

Строгая типизация серверного кода на TypeScript — tagged unions и Result<T,E>, generics для типобезопасных API-клиентов, strictNullChecks и optional chaining, exhaustive switch с never-проверкой, вывод типов из zod (z.infer). Use при написании или улучшении типов, обработке ошибок через Result, синхронизации zod-схем и типов, борьбе с any и небезопасными кастами.

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 11, 2026
developmenttypescriptapi

Works with

  • api

Security analysis

A100/100

Scanned September 11, 2026

npx -y skills add Vitammiin/agent-vorcl-flow --skill typescript --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Typescript?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Typescript
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/vitammiin-typescript/badge)](https://www.skillsdirectory.com/skills/vitammiin-typescript)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: typescript
description: "Строгая типизация серверного кода на TypeScript — tagged unions и Result<T,E>, generics для типобезопасных API-клиентов, strictNullChecks и optional chaining, exhaustive switch с never-проверкой, вывод типов из zod (z.infer). Use при написании или улучшении типов, обработке ошибок через Result, синхронизации zod-схем и типов, борьбе с any и небезопасными кастами."
---

# Навык: TypeScript

База: `"strict": true` в tsconfig (включает `strictNullChecks`, `noImplicitAny`). `any` запрещён; на границах (сеть, JSON, env) — `unknown` + сужение или zod.

## Tagged unions и Result<T, E>
Дискриминированное объединение — общее литеральное поле, по которому компилятор сужает тип:
```ts
type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E }

async function findUser(id: string): Promise<Result<User, 'NOT_FOUND' | 'DB_ERROR'>> {
  const row = await db.user.findUnique({ where: { id } })
  return row ? { ok: true, value: row } : { ok: false, error: 'NOT_FOUND' }
}

const res = await findUser(id)
if (!res.ok) return reply.code(404).send({ code: res.error }) // здесь res.error: 'NOT_FOUND' | 'DB_ERROR'
res.value                                                     // здесь точно User — компилятор сузил
```
Ожидаемые исходы (не найдено, конфликт, невалидно) — через `Result`, не через throw; исключения — для действительно исключительного (баги, обрыв соединения).

## Exhaustive switch (never-проверка)
Union разбирай switch'ом с `never`-веткой: добавил вариант и забыл обработать → ошибка компиляции, а не тихий баг в рантайме:
```ts
type OrderEvent = { type: 'created'; id: string } | { type: 'paid'; amount: number } | { type: 'cancelled' }

function handle(e: OrderEvent) {
  switch (e.type) {
    case 'created':   return onCreated(e.id)
    case 'paid':      return onPaid(e.amount)
    case 'cancelled': return onCancelled()
    default: { const _exhaustive: never = e; throw new Error(`Unhandled: ${JSON.stringify(_exhaustive)}`) }
  }
}
```

## Generics: типобезопасный API-клиент
Generics связывают вход и выход, чтобы вызывающий код получал точный тип без кастов:
```ts
async function request<TResponse, TBody = undefined>(
  method: 'GET' | 'POST' | 'PATCH' | 'DELETE',
  path: string,
  opts: { schema: z.ZodType<TResponse>; body?: TBody },
): Promise<Result<TResponse, ApiError>> {
  const res = await fetch(base + path, { method, body: opts.body && JSON.stringify(opts.body) })
  if (!res.ok) return { ok: false, error: await toApiError(res) }
  const parsed = opts.schema.safeParse(await res.json())      // рантайм-проверка формы ответа
  return parsed.success ? { ok: true, value: parsed.data } : { ok: false, error: invalidShape(parsed.error) }
}

const user = await request('GET', `/users/${id}`, { schema: UserSchema }) // Result<User, ApiError>
```
Никогда `as TResponse` на внешних данных — форму проверяет схема, тип выводится из неё.

## Null-safety
- `strictNullChecks`: `null`/`undefined` не проходят молча — обрабатывай явно.
- `?.` (optional chaining) и `??` (nullish coalescing) — вместо `&&`-цепочек и `||` (последний ловит `0`/`''` как falsy — баг).
- `!` (non-null assertion) — почти всегда запах: замени ранним выходом (`if (!x) return/throw`) — это сужает тип честно.
- Repository возвращает `T | null` (явное «не найдено»), списки — `[]`, не `null`/`undefined`.

## zod ↔ типы (единый источник)
Схема — источник истины, тип выводится через `z.infer`, рассинхрон невозможен:
```ts
const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1),
  plan: z.enum(['free', 'pro']).default('free'),
})
type CreateUserDto = z.infer<typeof CreateUserSchema> // { email: string; name: string; plan: 'free' | 'pro' }

const dto = CreateUserSchema.parse(req.body)          // parse/safeParse на границе — дальше тип честный
```
Не объявляй interface рядом со схемой вручную — двойное ведение разъедется. Граница всегда начинается со схемы: из типа схему не выведешь.

## Мелочи, дающие строгость
- `satisfies` — проверить форму, не расширяя тип: `const config = {…} satisfies AppConfig`.
- `as const` — литеральные union'ы из констант: `const ROLES = ['admin', 'user'] as const`.
- Утилити-типы вместо копипасты: `Pick`/`Omit`/`Partial`/`Record`, `ReturnType<typeof fn>`.
- `noUncheckedIndexedAccess` — индексный доступ возвращает `T | undefined`, ловит выход за границы массивов и словарей.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.