feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)
NOVAS SKILLS: - next-best-practices v0.1.0 (CLEAN) — Next.js App Router, RSC, caching, data - nextjs-patterns v1.0.0 (CLEAN) — Next.js 15: Server Actions, route handlers - vite v1.0.0 (CLEAN) — env vars, aliases, proxy, CJS compat - uncle-bob v1.0.0 (CLEAN) — Clean Code, SOLID, Clean Architecture - clean-code-review v1.0.0 (CLEAN) — naming, guard clauses, anti-patterns, refactoring - vue v1.0.0 (CLEAN) — Vue framework - vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos): - typescript/ — TS safe + generics gotchas - react/ — Next.js App Router + Vite config - vue/ — Composition API + Pinia - linux/ — System diagnostic cheatsheet - database/ — PostgreSQL + MySQL patterns - browser/ — Chromium CLI + E2E testing - security/ — SAST audit (OWASP Top 10) - best-practices/ — Clean Code + SOLID + Clean Architecture - deploy/ — Docker multi-stack + OpenClaw ops - + INDEX.md como guia de navegacao .learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
# TypeScript — Utility Types & Generics Gotchas
|
||||
|
||||
> Casos onde `Partial<T>`, `Omit`, `Pick`, `Extract`, etc. NÃO fazem o que você espera.
|
||||
|
||||
## Utility Type Gotchas
|
||||
|
||||
| Tipo | Armadilha | Solução |
|
||||
|------|----------|---------|
|
||||
| `Partial<T>` | *Shallow* — nested continua required | Criar `DeepPartial<T>` recursivo |
|
||||
| `Required<T>` | Não remove `undefined` da union | Usar `NonNullable<T[K]>` por campo |
|
||||
| `Omit<T, K>` | Não verifica se K existe — `Omit<User,"typo">` compila | Tipar com cuidado |
|
||||
| `Pick<T, K>` | Key inexistente também compila | Mesmo cuidado |
|
||||
| `Record<string, T>` | Implica TODA key existe — acesso a inexistente retorna `T`, não `T\|undefined` | Usar `Record<string, T \| undefined>` |
|
||||
| `Extract<T, U>` | Retorna `never` se não houver match — silenciosamente vazio | Checar se é `never` antes |
|
||||
| `ReturnType<typeof fn>` | Com overload pega só a última signature | Evitar overloads ou tipar retorno manualmente |
|
||||
| `NonNullable<T>` | Remove `null` E `undefined` — às vezes só quer um | Usar `Exclude<T, null>` ou `Exclude<T, undefined>` |
|
||||
| `Awaited<T>` | Desempacota recursivamente — surpresa com `Promise<Promise<T>>` | Cuidado com promises encadeadas |
|
||||
|
||||
## Generics — Armadilhas Práticas
|
||||
|
||||
```ts
|
||||
// ❌ <T = any> foge do any para todo o código
|
||||
function parse<T = any>(raw: string): T { ... }
|
||||
|
||||
// ✅ Deixe o caller fornecer o tipo, ou use unknown
|
||||
function parse<T>(raw: string): T { ... }
|
||||
// ou
|
||||
function parse(raw: string): unknown { ... }
|
||||
|
||||
// ❌ <T extends object> permite arrays
|
||||
function merge<T extends object>(a: T, b: T): T { ... }
|
||||
merge([1, 2], [3, 4]) // compila, mas não deve
|
||||
|
||||
// ✅ Use Record<string, unknown> para objetos puros
|
||||
function merge<T extends Record<string, unknown>>(a: T, b: T): T { ... }
|
||||
|
||||
// ❌ <T extends string> com literal infere string, não o literal
|
||||
function prefix<T extends string>(p: T) { ... }
|
||||
prefix("http") // T é "http", não "http" literal nesse contexto
|
||||
|
||||
// ✅ Use const assertion para preservar literais
|
||||
const urls = ["/api", "/auth"] as const; // readonly ["/api", "/auth"]
|
||||
```
|
||||
|
||||
## `keyof` em Função Genérica
|
||||
|
||||
```ts
|
||||
// ❌ keyof T em função genérica é sempre string | number | symbol
|
||||
function getProp<T, K extends keyof T>(obj: T, key: K) { ... }
|
||||
|
||||
// ✅ Para objeto puro, restringir:
|
||||
function getProp<T extends Record<string, unknown>, K extends keyof T>(
|
||||
obj: T, key: K
|
||||
): T[K] {
|
||||
return obj[key];
|
||||
}
|
||||
```
|
||||
|
||||
## Covariância e Contravariância
|
||||
|
||||
```ts
|
||||
// ❌ Arrays são COVARIANTES — Dog[] atribuível a Animal[] mas push(Cat) quebra runtime
|
||||
const dogs: Dog[] = [];
|
||||
const animals: Animal[] = dogs; // ✅ compila
|
||||
animals.push(new Cat()); // 🚨 runtime error
|
||||
|
||||
// ❌ Parâmetros de função são CONTRAVARIANTES
|
||||
type Handler = (animal: Animal) => void;
|
||||
const dogHandler: Handler = (dog: Dog) => { ... }; // ❌ não compila
|
||||
// (Animal) espera receber Animal, não só Dog
|
||||
```
|
||||
|
||||
## Mapped Types — Preservando Modificadores
|
||||
|
||||
```ts
|
||||
// ❌ { [K in keyof T]: T[K] } — PERDE readonly e optional
|
||||
type StrictClone<T> = { [K in keyof T]: T[K] };
|
||||
// → { readonly name: string } vira { name: string }
|
||||
|
||||
// ✅ Preservar modificadores com -readonly, -?
|
||||
type Preserve<T> = { -readonly [K in keyof T]: T[K] };
|
||||
type Optionalize<T> = { -? [K in keyof T]: T[K] };
|
||||
```
|
||||
|
||||
## Conditional Types — Distributividade
|
||||
|
||||
```ts
|
||||
// ✅ Distribui sobre union
|
||||
type NonNullableValues<T> = T extends null | undefined ? never : T;
|
||||
type Result = NonNullableValues<string | null | number | undefined>;
|
||||
// Result = string | number ← distribuiu corretamente
|
||||
|
||||
// ❌ Para não distribuir, usar [T] (tupla wrapper)
|
||||
type WrapNonNullable<T> = [T] extends [null | undefined] ? never : T;
|
||||
// Não distribui — útil quando a distribuição causa comportamento inesperado
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
# TypeScript — Safe Patterns
|
||||
|
||||
> Extraído de skills: typescript v1.0.2, generics.md, utility-types.md, declarations.md
|
||||
|
||||
## Anti-pattern: Evite `any`
|
||||
- Use `unknown` quando não souber o tipo — força narrowing antes do uso
|
||||
- Respostas de API: tipar ou `unknown`, nunca `any`
|
||||
|
||||
## Narrowing Failures
|
||||
|
||||
| Padrão | Problema | Solução |
|
||||
|--------|---------|---------|
|
||||
| `filter(Boolean)` | Não narrovia | `.filter((x): x is T => Boolean(x))` |
|
||||
| `Object.keys(obj)` | Retorna `string[]` | Usar `keyof typeof obj` é intencional — objetos podem ter chaves extras |
|
||||
| `Array.isArray()` | Narrovia para `any[]` | Pode precisar assertion de tipo de elemento |
|
||||
| `useState<User>()` | Infere `User \| undefined` | Tratar undefined inicial |
|
||||
| `Promise.all([a(), b()])` | Infere tupla só com `as const` | Usar `as const` na array |
|
||||
|
||||
## Literal Type Traps
|
||||
- `let x = "hello"` → `string`. Use `const` ou `as const` para literal
|
||||
- Propriedades de objeto alargam: `{ status: "ok" }` → `status: string`
|
||||
- Retorno de função alarga — anote explicitamente para retornos literais
|
||||
|
||||
## `satisfies` vs Type Annotation
|
||||
```ts
|
||||
// ❌ Perde info literal
|
||||
const config: RouteConfig = { path: "/home", method: "GET" };
|
||||
|
||||
// ✅ Mantém literal + valida compatibilidade
|
||||
const config = { path: "/home", method: "GET" } satisfies RouteConfig;
|
||||
```
|
||||
> Preferir `satisfies` para config objects.
|
||||
|
||||
## Strict Null Handling
|
||||
- `?.` retorna `undefined`, não `null`
|
||||
- `??` captura só `null`/`undefined`; `||` captura tudo falsy (`0`, `""`, `false`)
|
||||
- `!` (non-null assertion) — último recurso; preferir narrowing ou early return
|
||||
|
||||
## Generics — Armadilhas Comuns
|
||||
- `<T = any>` foge do `any` — restringe todo o código
|
||||
- `<T extends object>` permite arrays — usar `Record<string, unknown>` para objetos puros
|
||||
- Arrays são covariantes — `Dog[]` atribuível a `Animal[]` mas `push(Cat)` quebra em runtime
|
||||
- Parâmetros de função são contravariantes — `(Animal) => void` NÃO é atribuível a `(Dog) => void`
|
||||
- `Partial<T>` e `Required<T>` são *shallow* — não afetam nested:
|
||||
|
||||
```ts
|
||||
type DeepPartial<T> = {
|
||||
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
||||
};
|
||||
```
|
||||
|
||||
## Discriminated Unions
|
||||
```ts
|
||||
type Result<T> =
|
||||
| { ok: true; data: T }
|
||||
| { ok: false; error: string };
|
||||
|
||||
function handle(r: Result<User>) {
|
||||
if (r.ok) {
|
||||
console.log(r.data.name); // ✅ T automaticamente
|
||||
} else {
|
||||
console.log(r.error); // ✅ narrowed
|
||||
}
|
||||
}
|
||||
```
|
||||
- Switch exaustivo: `default: const _never: never = x` → erro de compilação se caso faltar
|
||||
|
||||
## Module Boundaries
|
||||
- `import type` — stripped em runtime, evita problemas de bundler
|
||||
- Re-export types: `export type { X }` — previne dependência runtime acidental
|
||||
- `.d.ts` augmentation: usar `declare module` com path exato do módulo
|
||||
|
||||
## Declaration File Gotchas
|
||||
- `declare module "x"` precisa de path EXATO — `"lodash"` ≠ `"lodash/index"`
|
||||
- `interface` pode ser mergeado de outros arquivos — `type` não pode
|
||||
- Export default em `.d.ts` é problemático — preferir named exports
|
||||
Reference in New Issue
Block a user