| name | jsdoc-authoring |
| description | JSDoc authoring skill for this repository. Use for robust JS-first contracts in Node 24 + ESM, with @ts-check, explicit @param/@returns, options typedefs, and no weak public tags. |
| license | MIT |
Skill — JSDoc Authoring
Overview
Use this skill when the task is to author or harden JSDoc in .js/.mjs/.cjs without migrating the
runtime wholesale to .ts.
The normative repository canon lives in:
This phase assumes:
- Node.js 24
- ESM /
NodeNext
allowJs + checkJs
- JSDoc as the public contract layer
When To Use
- Missing or weak public JSDoc
- Exported functions missing
@param or @returns
- Options objects documented as raw
object / Object
- Need to introduce
@import, @template, or @satisfies
- Need stronger IntelliSense without changing runtime semantics
When Not To Use
- The task is only conceptual and no file changes are needed
- The problem is a full strict-type failure better handled by
typing-node24-esm-tsserver
Inputs / Preconditions
- Read the target module first
- Inventory exports before editing
- Prefer official TypeScript-supported JSDoc, not generic JSDoc patterns that TS ignores
Workflow
- Add or preserve
// @ts-check at the top when the file is in scope.
- Document every exported function with complete
@param and @returns.
- For options objects, create a named
@typedef {object} and use it in the @param.
- Prefer
Record<string, unknown>, unions, and local typedefs over any.
- Use
@import or inline import('./file').Type when the type already exists elsewhere.
- Use
@template only when the API is truly generic.
- Use
@satisfies for object literals that must match a shared type without widening.
Protocolo Prático: Zerando Erros por Arquivo
Regra absoluta: // @ts-nocheck é proibido. Nunca use para suprimir erros.
Passo 1 — Triagem completa ANTES de editar
Nunca edite um arquivo sem antes mapear todos os erros agrupados por TS code. Edições cegas
causam regressões e erros em cascata.
npm run typecheck:strict:LANE 2>&1 \
| grep "nome-do-arquivo" \
| sed 's/.*nome-do-arquivo//' \
| sort -t: -k1,1n | uniq
npm run typecheck:strict:LANE 2>&1 | grep -oP 'TS\d+' | sort | uniq -c | sort -rn
npm run typecheck:strict:LANE 2>&1 \
| grep -oP '(?<=\/)[\w._-]+\.m?[jt]s' | sort | uniq -c | sort -rn | head -15
Passo 2 — Ler o arquivo inteiro (seções relevantes)
Sempre leia o arquivo antes de editar. Para arquivos grandes (> 400 linhas), faça leituras
paralelas das seções com erros + 20 linhas de contexto ao redor de cada linha listada. Leia
obrigatoriamente:
- Cabeçalho e imports (aliases, tipos importados)
- Todos os
class ... { constructor() {...} } com erros
- Funções/métodos com TS7006 (parâmetros sem tipo)
- Declarações
const arr = [] (possivelmente never[])
- Declarações
const obj = {} (possivelmente TS7053)
- Todos os
catch (err) blocks com TS18046
- Typedefs (
@typedef) com propriedades que cascateiam TS2339
Passo 3 — Classificar erros por tipo e prioridade de fix
| Prioridade | Código | Causa | Fix canônico |
|---|
| 1 (raiz) | TS8032 | Sub-@param sem pai | Remover ou adicionar @param pai |
| 1 (raiz) | TS8024 | @param fora de ordem | Reordenar @param / @typedef |
| 1 (raiz) | typedef | {{ tipo } sem }} | Corrigir {{...}} no typedef |
| 2 | TS7008 | Membro de classe any | /** @type {any[]} */ antes de this.x = [] |
| 2 | TS7034 | never[] em variável | /** @type {any[]} */ antes de const arr = [] |
| 2 | TS7053 | Indexação de {} | /** @type {Record<string,any>} */ antes do objeto |
| 3 | TS7006 | Param implícito any | Adicionar @param ao JSDoc do método |
| 3 | TS18046 | catch err é unknown | const _e = /** @type {any} */ (err); no catch |
| 4 (cascata) | TS2345 | Push em never[] (cascata) | Corrija o array upstream (TS7034/TS7008) |
| 4 (cascata) | TS2339 | Propriedade inexistente | Fix typedef upstream ou cast objeto para any |
| 4 (cascata) | TS2488 | @returns {object} não iterável | Mudar {object} → {any[]} no JSDoc |
| bugfix | TS2552 | object.fromEntries | Bug real: object → Object (maiúsculo) |
Passo 4 — Aplicar TODAS as correções do arquivo em uma única chamada
Use sempre multi_replace_string_in_file com todos os patches agrupados. Nunca faça
replace_string_in_file sequencial para o mesmo arquivo — é lento e falha se linhas deslocam.
Passo 5 — Verificar zero erros no arquivo
npm run typecheck:strict:LANE 2>&1 | grep "nome-do-arquivo" | wc -l
Passo 6 — Verificar lanes sempre-verdes
npm run typecheck:strict:src.logic 2>&1 | tail -3
npm run typecheck:strict:agents 2>&1 | tail -3
npm run typecheck:strict:scripts.ci 2>&1 | tail -3
Cookbook de Códigos de Erro TypeScript
TS7006 — Parâmetro implicitamente any
function process(data) { ... }
arr.map(n => n.trim())
function process(data) { ... }
arr.map( n => n.trim())
arr.forEach( v => action(v))
arr.filter( v => v.active)
Object.entries(obj).forEach(
(_, v) => use(v)
)
items.sort(( a, b) => a - b)
Padrão callback inline: /** @param {Tipo} nome */ nome => expr Esta sintaxe é suportada pelo
TypeScript dentro de .map(), .filter(), .forEach().
TS7008 — Membro de classe implicitamente any
class Foo {
constructor() {
this.items = [];
this.cache = {};
}
}
class Foo {
constructor() {
this.items = [];
this.cache = {};
}
}
Atalho para objetos grandes: quando uma classe acumula dados em muitos sub-arrays/objetos (ex:
this.issues = { unused: [], duplicates: [], enumCandidates: [], ... }), anotar o objeto inteiro
com sua forma completa elimina cascata de never[] (TS2345) em todos os .push() da classe:
this.issues = {
unused: [],
duplicates: [],
magicValues: [],
enumCandidates: [],
typeCandidates: [],
};
TS7034 — Variável never[]
const results = [];
const deps = [];
const results = [];
const deps = [];
TS7053 — Indexação de objeto {}
const byFile = {};
const types = {};
const byFile = {};
const types = {};
const icons = {};
TS18046 — catch (err) — err é unknown
} catch (err) {
console.error(err.message);
}
} catch (err) {
const _e = (err);
console.error(_e.message);
}
⚠️ Atenção: Se o catch block contém emoji no template string (ex: ⚠️ Erro: ${err.message}), o
replace_string_in_file pode falhar com encoding. Use um script Python:
import re, pathlib
f = pathlib.Path('arquivo.mjs')
txt = f.read_text(encoding='utf-8')
txt = re.sub(
r'\} catch \(err\) \{([\s\S]*?)\}',
lambda m: '} catch (err) {\n const _e = /** @type {any} */ (err);' +
m.group(1).replace('err.message', '_e.message').replace('err.stack', '_e.stack') + '}',
txt
)
f.write_text(txt, encoding='utf-8')
TS2339 — Propriedade não existe no tipo
const snippet = ({
prefix: opts.prefix,
include: opts.include,
});
const analysisData = {
files: [],
issues: { unused: [], duplicates: [] },
};
Não confundir com typedef incompleto: se apenas alguns @property faltam no typedef, adicione-os
ao @typedef em vez de usar any. Prefira types reais quando a forma é conhecida.
TS2345 — Argumento incompatível (SEMPRE corrija o upstream)
results.push( (item));
allVars.push(...result.variables);
const allVars = [];
allVars.push(...result.variables);
TS2488 — Tipo não iterável (@returns {object})
function scanFile() {
return [];
}
function scanFile() {
return [];
}
TS2552 — Nome não encontrado (case sensitivity = bug real)
object.fromEntries(entries);
Object.fromEntries(entries);
Typedef JSDoc malformado com {{ ... } sem }}
* @property {{ imports?: string[], patterns?: string[], sequences?: string[], minHits?: number }} candidate
Ordem de Cascata: Corrija Causas, não Sintomas
Regra fundamental: TS2345, TS2322, TS2339 são sintomas de causas primárias (TS7034, TS7053,
TS7008, TS8032). Corrija sempre os primários — os de cascata desaparecem automaticamente.
Ordem canônica dentro de um arquivo:
- JSDoc malformados (TS8032, TS8024) → 0
- Typedefs malformados (
{{ tipo } sem }}) → 0
- Membros de constructor (TS7008) → 0 → elimina TS2345 em pushes
- Arrays
never[] (TS7034) → 0 → elimina TS2345 em spreads e pushes
- Objetos
{} sem type (TS7053) → 0
- Parâmetros de métodos/funções (TS7006) → 0
- Catch blocks (TS18046) → 0
- TS2339 e TS2345 residuais → 0
Estratégia de Edição em Lote
Uma única chamada multi_replace_string_in_file por arquivo
Agrupe todos os patches de um arquivo numa única chamada. Vantagens:
- Edições sequenciais individuais falham se a primeira deslocou linhas
- Uma única chamada é 5–10× mais rápida
- Correspondência (
oldString) é mais segura em contexto fresco
Para arquivos grandes (> 500 linhas): leituras paralelas
read_file(1-100) + read_file(180-240) [paralela]
read_file(430-480) + read_file(760-820) [paralela]
Depois aplique todos os patches de uma vez.
Inclua 3–5 linhas de contexto no oldString
constructor(config) { ← contexto abaixo
this.config = config;
Casos Especiais
Callback em .map() / .filter() / .forEach()
TypeScript strict mode às vezes não infere o tipo do callback mesmo quando o array é string[]. Use
a anotação inline:
names.map( (n) => n.trim());
items.filter( (v) => v.active);
entries.map( (v) => ({ name: v.name, file: v.file }));
new Set(arr.map( (f) => path.basename(f)));
Objetos grandes com muitas propriedades
Quando um objeto acumula 10+ propriedades (analysisData, state, config), anotar o objeto
inteiro como /** @type {any} */ elimina dezenas de TS7008/TS2339/TS7053 com uma linha:
const analysisData = {
files: [],
issues: { unused: [], duplicates: [] },
enumCandidates: new Map(),
};
Classe que recebe analysisData ou objeto any
Quando uma classe encapsula dados provenientes de um objeto any, tipar o param do construtor como
/** @param {any} data */ faz this.data ser any, eliminando todos os TS2339 nos métodos:
class ReportGenerator {
constructor(data) {
this.data = data;
}
}
Membro this.issues com múltiplos sub-arrays
Type annotation completa no construtor elimina cascata de never[] em todos os .push():
this.issues = {
unused: [],
duplicates: [],
magicValues: [],
redundantLet: [],
enumCandidates: [],
typeCandidates: [],
};
@returns {object} → iteração for...of
Se o código faz for (const x of fn()) e o JSDoc tem @returns {object}, mude para
@returns {any[]}. TypeScript não considera object iterável.
Guardrails
- Do not use
Object, Array, or Function in a public contract when a real shape is knowable.
- Do not add
@throws unless the code path genuinely throws or propagates.
- Do not use generated placeholder comments as the canonical result.
- Do not change runtime behavior just to simplify the docs.
Validation / Done Criteria
Metas numéricas do programa full-strict (todas devem ser = 0 simultaneamente):
| Métrica | Alvo |
|---|
functions_missing_param_tags | 0 |
functions_missing_returns | 0 |
unsafe_generic_tags_total | 0 |
public_any_tags_total | 0 |
options_objects_without_typedef | 0 |
Critérios qualitativos (aplica em toda PR):
- Cada exportação pública tem
@returns documentado.
- Parâmetros públicos estão totalmente tagueados com
@param {type}.
- Objetos de opções usam
@typedef {object} nomeado, nunca Object genérico.
- JSDoc público evita tags genéricas fracas (
any, object, Function) salvo contrato dinâmico
intencional e documentado.
Related Skills