circumspect
es un conjunto de funciones para hacer que su código TypeScript/JavaScript sea más seguro. Estas funciones incluyen invariant
, warning
, assertNever
y más.
Hay muchas funciones útiles que pueden hacer que su código sea más seguro, como invariant
y assertNever
. Muchas de estas funciones están disponibles como paquetes npm separados (por ejemplo, invariant
). Pero instalar un nuevo paquete (y la mayoría de las veces también el equivalente @types
) para cada función no es muy conveniente. Además, esos paquetes generalmente exportan sus funciones como exportaciones predeterminadas, lo que hace que la importación automática de VSCode no funcione bien con ellos.
Esta biblioteca unifica las funciones que hacen que su código sea más seguro en un solo lugar. circumspect
es una sola dependencia que los tiene todos. Y la importación automática de VSCode funciona como se esperaba porque todas las funciones proporcionadas por circumspect
se denominan exportaciones.
Además, circumspect
está escrito 100% en TypeScript, por lo que cada función se escribe correctamente de forma predeterminada. Por lo tanto, no es necesario instalar paquetes @types
separados.
Usando hilo:
yarn add circumspect
Usando npm:
npm install circumspect
invariant
warning
assertNever
nonNull
invariant
invariant ( value : unknown , message ?: string ) : asserts value
value: unknown
es el valor que queremos asegurar que sea veraz.
message?: string
es un mensaje de error opcional que se incluirá en el error que se genera cuando el value
pasado es falso. Este mensaje de error personalizado se muestra solo en desarrollo. En producción, en su lugar se muestra un error genérico ( 'Invariant violation'
) y se ignora el parámetro message
.
asserts value
, lo que significa que la función simplemente limita el tipo de value
para que sea verdadero y no devuelve nada. invariant
garantiza que el value
especificado sea verdadero. Si ese no es el caso, arroja un error que contiene el message
especificado (solo en desarrollo). Esta función limita adecuadamente el tipo de value
al excluir todos los valores falsos (por ejemplo, null
e undefined
).
invariant
debe usarse siempre que tengamos un valor que podría ser potencialmente falso, pero estemos seguros de que, en una circunstancia particular, el valor debe ser verdadero. Es decir, el hecho de que el valor sea verdadero es un invariante, y si alguna vez sucede que el valor es falso, el invariante ha sido violado y, por lo tanto, se debe generar un error.
Dado que el argumento message
se ignora por completo en producción, es posible que desee eliminarlo por completo de su código en las compilaciones de producción. Para ver cómo hacerlo, consulte la sección Optimizaciones.
declare const user : User | null | undefined ;
invariant ( user , 'The user is missing!' ) ;
user ; // If we get here, the type is narrowed to `User`
En este ejemplo, el objeto user
puede ser potencialmente null
o undefined
(es decir, falso). Si ese es el caso, tenemos una violación invariante y la función invariant
arrojará. De lo contrario, simplemente regresa y sabemos que user
en realidad apunta a un objeto de usuario.
warning
warning ( value : unknown , message : string ) : void
value: unknown
es el valor que queremos comprobar. Si es falso, se emite una advertencia a la consola.
message: string
es el mensaje de advertencia que se escribirá en la consola.
void
la función no devuelve nada. warning
emite una advertencia a la consola con el message
dado si el value
especificado es falso. La advertencia se emite sólo en desarrollo. En producción, esta función no hace nada.
Debe usarse siempre que queramos emitir una advertencia solo de desarrollo si algún valor es falso, lo que debería ayudar a guiar a los desarrolladores a solucionar los problemas no críticos que se informan en el mensaje de advertencia.
Dado que warning
no hace nada en producción, es posible que desee eliminar completamente las llamadas a esta función de su código en las compilaciones de producción. Para ver cómo hacerlo, consulte la sección Optimizaciones.
declare const mode : 'auto' | 'default' | 'slow' | 'fast' ;
warning (
mode !== 'auto' ,
'Mode "auto" has been deprecated. Please use "default" instead.' ,
) ;
En este ejemplo, queremos emitir una advertencia de obsolescencia si el mode
es 'auto'
. Para hacerlo, necesitamos pasar un valor falso, es por eso que pasamos mode !== 'auto'
, que es falso solo cuando el mode
es 'auto'
.
En algunos casos, tiene sentido pasar false
directamente. Por ejemplo:
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
es el valor después de que se hayan agotado todas las variantes posibles de tipo de unión. never
lo que significa que la función nunca regresa; simplemente arroja un error si alguna vez se llama. assertNever
debe usarse para garantizar que se hayan agotado todas las variantes de un tipo de unión.
Si se han agotado todas las variantes de unión de value
, no hay ningún error del compilador al llamar assertNever
con value
, ya que se considera que value
es de tipo never
en ese punto, y en tiempo de ejecución, nunca llegamos al punto de llamar assertNever
, lo que significa que no se generará ningún error.
Sin embargo, si no se han agotado todas las variantes de unión, entonces llamaremos assertNever
con algo distinto de never
y, por lo tanto, habrá un error del compilador que dirá algo como
Argument of type 'x' is not assignable to parameter of type 'never'.
que podemos solucionar manejando las variantes que faltan. Puede leer más sobre la verificación de exhaustividad de la unión y assertNever
en los documentos de TypeScript.
declare const state : 'loading' | 'done' | 'error' ;
switch ( state ) {
case 'loading' :
return < Loading / > ;
case 'done' :
return < Done / > ;
case 'error' :
return < Error / > ;
}
En este ejemplo, manejamos todos los estados posibles dentro de la declaración switch
: 'loading'
, 'done'
y 'error'
.
Sin embargo, ¿qué pasa si en el futuro agregamos otro estado, como 'pending'
?
El hecho de que la declaración switch
no maneje 'pending'
no se detectaría.
La solución es tener un caso default
en el que afirmemos que se han manejado todos los estados posibles.
switch ( state ) {
...
default :
return assertNever ( state ) ;
}
Entonces, cuando se manejan todas las variantes de estado, no obtenemos ningún error en tiempo de compilación. Sin embargo, cuando agregamos el nuevo estado 'pending'
, obtendremos un error del compilador que dice:
Argument of type 'string' is not assignable to parameter of type 'never'.
Podemos corregir este error manejando el estado 'pending'
dentro del switch
.
Como puede ver en este ejemplo, assertNever
es especialmente útil en declaraciones switch
donde queremos asegurarnos de haber manejado todos los casos posibles en todo momento.
nonNull
nonNull < T > ( value : T | null | undefined ) : value is T
value: T | null | undefined
es el valor que queremos comprobar para que no sea null
o undefined
. value is T
, lo que significa que la función devuelve un valor booleano que indica si el valor pasado no es null
ni undefined
. Esto reduce el tipo de value
a T
(es decir, excluye null
e undefined
) cuando se devuelve true
. nonNull
es una función de predicado que comprueba si el valor especificado no es nulo, es decir, ni null
ni undefined
. Después de llamar a la función, el tipo de value
se limita adecuadamente en función de si se devolvió true
o false
.
nonNull
debe usarse siempre que tengamos un valor que potencialmente podría ser null
, undefined
o ambos y queremos verificarlo y limitar su tipo adecuadamente.
El nombre de esta función proviene del tipo de utilidad NonNullable
, que excluye null
e undefined
de un tipo.
declare const names : ( string | null ) [ ] ;
const nonNullNames = names . filter ( nonNull ) ;
// nonNullNames has type 'string[]'
En este ejemplo, tenemos una matriz de nombres que también puede incluir algunos elementos null
. Filtramos la matriz para todos los elementos no nulos y obtenemos una string[]
.
Si en su lugar usáramos names.filter(x => x !== null)
, obtendríamos elementos no nulos, pero el tipo seguiría siendo (string | null)[]
ya que TypeScript ve la función que pasamos para filter
como simplemente devolver un boolean
y, por lo tanto, no se produce ninguna reducción de tipos.
Recomendamos usar babel-plugin-dev-expression
para eliminar el argumento message
pasado a invariant
y eliminar por completo las llamadas a warning
en producción.
Básicamente, en producción, babel-plugin-dev-expression
reemplaza
invariant ( value , 'Value is falsy!' ) ;
con
if ( ! value ) {
invariant ( false ) ;
}
por lo que se elimina el argumento message
. También elimina por completo las llamadas de warning
. Entonces, líneas como esta
warning ( value , 'Value is falsy!' ) ;
son eliminados.
Las solicitudes de extracción son bienvenidas. Si tiene la intención de introducir un cambio importante, primero abra un problema relacionado en el que podamos discutir lo que le gustaría cambiar.
Asegúrese de actualizar las pruebas y el archivo README según corresponda.
MIT