O tipo dos elementos não garante a posição
Um string[] descreve o tipo dos elementos que a coleção fornece, mas pode estar vazio. noUncheckedIndexedAccess acrescenta undefined a leituras indexadas que não têm uma posição garantida. A opção deve ser declarada explicitamente: não se deve concluir que está ativa só porque strict é true. Uma assinatura de índice numérica ou textual também não garante que todas as chaves possíveis existam.
Ao rever um acesso, identifica a garantia concreta: a posição está declarada numa tupla, foi obtida por iteração ou foi calculada a partir de um pedido? No último caso, a ausência pode ser normal e merece um ramo. Uma assertion ! remove a possibilidade do tipo usado pelo chamador, mas não cria um elemento em falta.
Guardar e refinar a leitura
Com a opção de acesso indexado ativo, uma condição i < rows.length num ciclo não faz desaparecer automaticamente o possível undefined de rows[i]. Uma solução útil é guardar o valor, verificar a ausência e só então ler as suas propriedades. Assim, o refinamento diz respeito à leitura que será efetivamente usada.
A política depende do contrato. Se cada posição do intervalo tem de existir, uma posição ausente deve produzir um erro de domínio. Trocar para uma operação que ignore posições ausentes mudaria esse requisito. Quando apenas interessa percorrer elementos de uma coleção válida e densa, uma iteração pelos valores pode exprimir melhor o objetivo. Escolhe a forma pelo comportamento exigido, não apenas para remover o aviso.
Tuplas conservam posições conhecidas
Uma tupla readonly [string, number] declara duas posições obrigatórias com tipos diferentes. Um índice literal 1 seleciona number sem precisar de verificar um comprimento arbitrário. Um índice literal fora da tupla é um erro. Isto é mais preciso do que um array de string | number quando cada posição tem um significado diferente.
Posições opcionais alteram essa precisão. Em readonly [number, number, number?], a terceira leitura admite undefined e o comprimento pode ser 2 ou 3. readonly impede escrita através dessa tupla, mas não acrescenta posições. Se uma função só lê uma tupla criada com as const, a sua assinatura deve aceitar uma tupla readonly, evitando uma exigência de mutabilidade de que não precisa.
Zero e ausência são decisões distintas
Num limite de repetição, zero pode significar uma decisão explícita de não repetir. A expressão count || 3 substitui zero porque o trata como falsy. count ?? 3 aplica a alternativa apenas perante null ou undefined, conservando zero. Se o modelo só admite number | undefined, também se pode testar undefined explicitamente.
Um valor predefinido não valida o resto do domínio. NaN, um valor fracionário ou um limite negativo podem continuar a precisar de rejeição. Se os valores do mapa já foram validados, a leitura só precisa de tratar a ausência prevista; se chegam de uma fronteira externa, a validação tem de acontecer antes de a aplicação confiar no tipo number. Não mistures as duas responsabilidades numa assertion.
A proteção tem um alcance definido
config?.retry.limit protege a ausência de config, mas não uma ausência independente de retry quando config existe. Cada nível opcional exige uma decisão. Pode usar-se outra cadeia opcional e depois tratar o resultado ausente, ou rejeitar uma configuração incompleta antes de a usar. A forma mais curta só é correta se representar a política pretendida.
A cadeia opcional também não protege operações exteriores. Em item?.amount / computeScale(), a chamada computeScale continua a ser um operando separado. Com strictNullChecks, a possível divisão de undefined é sinalizada. Se a intenção for evitar a chamada quando item falta, o fluxo precisa de um ramo explícito; não basta acrescentar ?. ao acesso inicial.
readonly, aliases e operações que alteram a ordem
Um parâmetro readonly number[] pode receber um array mutável porque a função promete usá-lo através de uma interface de leitura. Isso não impede o proprietário de o alterar por outro alias. Também não cria uma cópia. Alterações legítimas do proprietário continuam visíveis na vista que partilha a mesma referência.
sort altera o próprio array. Para produzir uma ordem de apresentação sem mudar a ordem partilhada, copia o array e ordena a cópia. Com números, a cópia exterior basta para separar a ordem. Com objetos, os elementos continuam partilhados e pode ser necessária outra política caso também se alterem campos. Aplicar as number[] a uma entrada readonly apenas esconde essa responsabilidade.
Leitura guiada de uma seleção
selectRow parte de linhas simples já validadas. Primeiro recusa índices que não sejam inteiros seguros não negativos. Depois guarda rows[index] e distingue a posição ausente. Só no ramo restante constrói uma linha nova com os dois campos primitivos. A função não usa ! nem promete que qualquer índice numérico existe.
Segue os resultados para -1, para um índice para lá do fim e para uma linha cujo retries é zero. O zero é preservado porque nenhum teste de truthiness o substitui. A cópia separa os campos do resultado das alterações futuras da linha de origem, mas não torna o próprio resultado imutável. Um requisito de escrita ou de validação adicional deve ser expresso separadamente.
// TypeScript 7.0; strict: true; noUncheckedIndexedAccess: true; ES2022 library.
// Validated plain rows, dense array, no extra numeric properties.
type Row = { id: string; retries: number };
type Selection =
| { ok: true; row: Row }
| { ok: false; reason: "invalid-index" | "missing-row" };
function selectRow(rows: readonly Row[], index: number): Selection {
if (!Number.isSafeInteger(index) || index < 0) {
return { ok: false, reason: "invalid-index" };
}
const row = rows[index];
if (row === undefined) {
return { ok: false, reason: "missing-row" };
}
return { ok: true, row: { id: row.id, retries: row.retries } };
}Lê três casos: índice negativo, índice para lá do fim e índice presente. A leitura é guardada antes do refinamento. O resultado aceite copia os dois campos primitivos, pelo que alterar a linha de origem depois não altera essa cópia.
Armadilhas comuns
Usar ! em cada índice; esperar que readonly congele outros aliases; aplicar || a zero válido; esquecer que ?. só protege a cadeia indicada.
Tópicos relacionados: Uniões, refinamento de tipos e modelos de estado · Objetos e compatibilidade estrutural · Dados externos e fronteiras de erro
Uma coleção tipada ainda tem limites, ausência e identidade. Verifica a leitura usada e decide explicitamente se o resultado partilha ou copia dados.
Referência: TypeScript: unchecked · TypeScript 7.0; explicit strict and host configuration