circumspect
é um conjunto de funções para tornar seu código TypeScript/JavaScript mais seguro. Essas funções incluem invariant
, warning
, assertNever
e muito mais.
Existem muitas funções úteis que podem tornar seu código mais seguro, como invariant
e assertNever
. Muitas dessas funções estão disponíveis como pacotes npm separados (por exemplo, invariant
). Mas instalar um novo pacote (e na maioria das vezes também o equivalente @types
) para cada função não é muito conveniente. Além disso, esses pacotes geralmente exportam suas funções como exportações padrão, fazendo com que a importação automática do VSCode não funcione bem com eles.
Esta biblioteca unifica as funções que tornam seu código mais seguro em um único lugar. circumspect
é uma única dependência que possui todos eles. E a importação automática do VSCode funciona conforme o esperado porque todas as funções fornecidas pelo circumspect
são denominadas exportações.
Além disso, circumspect
é 100% escrito em TypeScript, portanto, cada função é digitada corretamente por padrão. Portanto, não há necessidade de instalar pacotes @types
separados.
Usando fio:
yarn add circumspect
Usando npm:
npm install circumspect
invariant
warning
assertNever
nonNull
invariant
invariant ( value : unknown , message ?: string ) : asserts value
value: unknown
é o valor que queremos garantir que seja verdadeiro.
message?: string
é uma mensagem de erro opcional que será incluída no erro gerado quando o value
passado for falso. Esta mensagem de erro personalizada é exibida apenas em desenvolvimento. Na produção, um erro genérico é mostrado ( 'Invariant violation'
) e o parâmetro message
é ignorado.
asserts value
, o que significa que a função simplesmente restringe o tipo de value
para ser verdadeiro e não retorna nada. invariant
garante que o value
especificado seja verdadeiro. Se não for esse o caso, será gerado um erro contendo a message
especificada (somente em desenvolvimento). Esta função restringe adequadamente o tipo de value
, excluindo todos os valores falsos (por exemplo, null
e undefined
).
invariant
deve ser usado sempre que tivermos um valor que possa ser potencialmente falso, mas estamos confiantes de que, em uma circunstância particular, o valor deve ser verdadeiro. Ou seja, o fato de o valor ser verdadeiro é um invariante, e se acontecer de o valor ser falso, o invariante foi violado e, portanto, um erro deve ser lançado.
Como o argumento message
é completamente ignorado na produção, você pode querer retirá-lo completamente do seu código nas compilações de produção. Para ver como fazer isso, consulte a seção Otimizações.
declare const user : User | null | undefined ;
invariant ( user , 'The user is missing!' ) ;
user ; // If we get here, the type is narrowed to `User`
Neste exemplo, o objeto user
pode ser potencialmente null
ou undefined
(ou seja, falso). Se for esse o caso, temos uma violação invariante e a função invariant
será lançada. Caso contrário, ele simplesmente retorna e sabemos que user
realmente aponta para um objeto de usuário.
warning
warning ( value : unknown , message : string ) : void
value: unknown
é o valor que queremos verificar. Se for falso, um aviso será emitido para o console.
message: string
é a mensagem de aviso que será gravada no console.
void
a função não retorna nada. warning
emite um aviso para o console com a message
fornecida se o value
especificado for falso. O aviso é emitido apenas em desenvolvimento. Na produção, esta função não faz nada.
Deve ser usado sempre que quisermos emitir um aviso somente de desenvolvimento se algum valor for falso, o que deve ajudar a orientar os desenvolvedores a corrigir os problemas não críticos relatados na mensagem de aviso.
Como warning
não faz nada na produção, você pode querer remover completamente as chamadas para essa função do seu código nas compilações de produção. Para ver como fazer isso, consulte a seção Otimizações.
declare const mode : 'auto' | 'default' | 'slow' | 'fast' ;
warning (
mode !== 'auto' ,
'Mode "auto" has been deprecated. Please use "default" instead.' ,
) ;
Neste exemplo, queremos emitir um aviso de depreciação se o mode
for 'auto'
. Para fazer isso, precisamos passar um valor falso, por isso passamos mode !== 'auto'
, que é falso apenas quando o mode
é 'auto'
.
Em alguns casos, faz sentido passar false
diretamente. Por exemplo:
declare const languages : Language [ ] ;
declare const defaultLanguage : Language ;
declare const langName : string ;
let lang = languages . find ( ( { name } ) => name === langName ) ;
if ( ! lang ) {
warning (
false ,
`Language with name " ${ langName } " not found. Falling back to the default language.` ,
) ;
lang = defaultLanguage ;
}
assertNever
assertNever ( value : never ) : never
value: never
é o valor após todas as variantes possíveis do tipo de união terem sido esgotadas. never
o que significa que a função nunca retorna; apenas gera um erro se for realmente chamado. assertNever
deve ser usado para garantir que todas as variantes de um tipo de união foram esgotadas.
Se todas as variantes de união de value
foram esgotadas, não há erro do compilador ao chamar assertNever
com value
, já que value
é considerado do tipo never
naquele ponto e, em tempo de execução, nunca chegamos ao ponto de chamar assertNever
, o que significa que nenhum erro será lançado.
No entanto, se nem todas as variantes de união tiverem sido esgotadas, então estaremos chamando assertNever
com algo diferente de never
e, portanto, haverá um erro do compilador dizendo algo como
Argument of type 'x' is not assignable to parameter of type 'never'.
que podemos corrigir lidando com as variantes ausentes. Você pode ler mais sobre verificação de exaustividade de união e assertNever
na documentação do TypeScript.
declare const state : 'loading' | 'done' | 'error' ;
switch ( state ) {
case 'loading' :
return < Loading / > ;
case 'done' :
return < Done / > ;
case 'error' :
return < Error / > ;
}
Neste exemplo, tratamos todos os estados possíveis dentro da instrução switch
: 'loading'
, 'done'
e 'error'
.
No entanto, e se no futuro adicionarmos outro estado, como 'pending'
?
O fato de a instrução switch
não estar lidando com 'pending'
não seria detectado.
A solução é ter um caso default
no qual afirmamos que todos os estados possíveis foram tratados.
switch ( state ) {
...
default :
return assertNever ( state ) ;
}
Portanto, quando todas as variantes de estado são tratadas, não obtemos nenhum erro em tempo de compilação. No entanto, quando adicionarmos o novo estado 'pending'
, obteremos um erro do compilador dizendo:
Argument of type 'string' is not assignable to parameter of type 'never'.
Podemos corrigir esse erro manipulando o estado 'pending'
dentro do switch
.
Como você pode ver neste exemplo, assertNever
é especialmente útil em instruções switch
onde queremos garantir que tratamos todos os casos possíveis em todos os momentos.
nonNull
nonNull < T > ( value : T | null | undefined ) : value is T
value: T | null | undefined
é o valor que queremos verificar para não ser null
ou undefined
. value is T
, o que significa que a função retorna um booleano indicando se o valor passado não é null
nem undefined
. Isso restringe o tipo de value
para T
(ou seja, exclui null
e undefined
) quando true
é retornado. nonNull
é uma função de predicado que verifica se o valor especificado é não nulo, ou seja, nem null
nem undefined
. Depois de chamar a função, o tipo de value
é devidamente reduzido com base no retorno de true
ou false
.
nonNull
deve ser usado sempre que tivermos um valor que possa ser potencialmente null
, undefined
ou ambos e quisermos verificar isso e ter seu tipo devidamente reduzido.
O nome desta função deriva do tipo de utilitário NonNullable
, que exclui null
e undefined
de um tipo.
declare const names : ( string | null ) [ ] ;
const nonNullNames = names . filter ( nonNull ) ;
// nonNullNames has type 'string[]'
Neste exemplo, temos um array de nomes que também pode incluir alguns elementos null
. Filtramos o array para todos os elementos não nulos e recuperamos um string[]
.
Se, em vez disso, usássemos names.filter(x => x !== null)
, receberíamos elementos não nulos, mas o tipo ainda seria (string | null)[]
já que o TypeScript vê a função que passamos para filter
como apenas retornar um boolean
e, portanto, nenhum estreitamento de tipo ocorre.
Recomendamos o uso babel-plugin-dev-expression
para remover o argumento message
passado para invariant
e para remover completamente as chamadas para warning
na produção.
Basicamente, na produção, babel-plugin-dev-expression
substitui
invariant ( value , 'Value is falsy!' ) ;
com
if ( ! value ) {
invariant ( false ) ;
}
então o argumento message
é removido. Também remove completamente as chamadas para warning
. Então, linhas como esta
warning ( value , 'Value is falsy!' ) ;
são removidos.
Solicitações pull são muito bem-vindas. Se você pretende introduzir uma mudança importante, abra primeiro uma questão relacionada na qual possamos discutir o que você gostaria de mudar.
Certifique-se de atualizar os testes e o README conforme apropriado.
MIT