circumspect
是一组使您的 TypeScript/JavaScript 代码更安全的函数。这些函数包括invariant
、 warning
、 assertNever
等。
有许多有用的函数可以使您的代码更安全,例如invariant
和assertNever
。其中许多函数都可以作为单独的 npm 包提供(例如invariant
)。但是为每个函数安装一个新的包(大多数时候还有@types
对应的包)并不是很方便。此外,这些包通常将其函数导出为默认导出,使得 VSCode 自动导入无法与它们很好地配合。
该库将使您的代码更安全的功能统一到一个地方。 circumspect
是一个拥有所有这些的单一依赖。 VSCode 自动导入按预期工作,因为由circumspect
提供的所有功能都被命名为导出。
此外, circumspect
是 100% 用 TypeScript 编写的,因此默认情况下每个函数都是正确键入的。因此,无需安装单独的@types
包。
使用纱线:
yarn add circumspect
使用 npm:
npm install circumspect
invariant
warning
assertNever
nonNull
invariant
invariant ( value : unknown , message ?: string ) : asserts value
value: unknown
是我们要确保真实的值。
message?: string
是一个可选的错误消息,当传递的value
假时,该消息将包含在引发的错误中。此自定义错误消息仅在开发中显示。在生产中,会显示一般错误( 'Invariant violation'
),并且message
参数将被忽略。
asserts value
意味着该函数只是将value
的类型缩小为真值并且不返回任何内容。 invariant
确保指定的value
是真实的。如果不是这种情况,它会抛出包含指定message
错误(仅在开发中)。该函数通过排除所有虚假值(例如null
和undefined
)来正确缩小value
的类型。
每当我们有一个可能是假的值,但我们确信在特定情况下,该值一定是真的时,就应该使用invariant
。也就是说,该值是真的这一事实是一个不变量,如果发生该值是假的,则该不变量被违反,因此必须抛出错误。
由于message
参数在生产中被完全忽略,因此您可能希望在生产构建中将其从代码中完全删除。要了解如何执行此操作,请参阅优化部分。
declare const user : User | null | undefined ;
invariant ( user , 'The user is missing!' ) ;
user ; // If we get here, the type is narrowed to `User`
在此示例中, user
对象可能为null
或undefined
(即,假)。如果是这种情况,我们就会遇到不变违规,并且invariant
函数将抛出异常。否则,它只是返回,我们知道user
实际上指向一个用户对象。
warning
warning ( value : unknown , message : string ) : void
value: unknown
是我们要检查的值。如果它是假的,则会向控制台发出警告。
message: string
是将写入控制台的警告消息。
void
该函数不返回任何内容。 如果指定的value
假,则warning
会向控制台发出警告并给出给定的message
。该警告仅在开发时发出。在生产中,此函数不执行任何操作。
每当我们想要在某些值是假的情况下发出仅开发警告时,就应该使用它,这应该有助于指导开发人员修复警告消息中报告的非关键问题。
由于warning
在生产中不会执行任何操作,因此您可能希望从生产构建中的代码中完全删除对此函数的调用。要了解如何执行此操作,请参阅优化部分。
declare const mode : 'auto' | 'default' | 'slow' | 'fast' ;
warning (
mode !== 'auto' ,
'Mode "auto" has been deprecated. Please use "default" instead.' ,
) ;
在此示例中,如果mode
为'auto'
我们希望发出弃用警告。为此,我们需要传递一个假值,这就是为什么我们传递mode !== 'auto'
,只有当mode
为'auto'
时,它才是假值。
在某些情况下,直接传递false
是有意义的。例如:
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
是用尽所有可能的联合类型变体后的值。 never
表示该函数永远不会返回;如果实际调用过,它只会抛出一个错误。 应该使用assertNever
来确保联合类型的所有变体都已用尽。
如果value
的所有联合变体都已用尽,则在使用value
调用assertNever
时不会出现编译器错误,因为此时value
被认为是never
类型,并且在运行时,我们永远不会达到调用assertNever
的点,这意味着不会抛出任何错误。
但是,如果尚未用尽所有联合变体,那么我们将使用never
之外的其他内容来调用assertNever
,因此会出现编译器错误,提示类似以下内容
Argument of type 'x' is not assignable to parameter of type 'never'.
我们可以通过处理缺失的变体来修复它。您可以在 TypeScript 文档中阅读有关联合详尽性检查和assertNever
更多信息。
declare const state : 'loading' | 'done' | 'error' ;
switch ( state ) {
case 'loading' :
return < Loading / > ;
case 'done' :
return < Done / > ;
case 'error' :
return < Error / > ;
}
在此示例中,我们处理switch
语句内的所有可能状态: 'loading'
、 'done'
和'error'
。
但是,如果将来我们添加另一个状态(例如'pending'
怎么办?
switch
语句不处理'pending'
的事实将不会被检测到。
解决方案是有一个default
情况,在该情况下我们断言所有可能的状态都已被处理。
switch ( state ) {
...
default :
return assertNever ( state ) ;
}
因此,当处理所有状态变体时,我们不会遇到编译时错误。但是,当我们添加新的'pending'
状态时,我们将收到编译器错误:
Argument of type 'string' is not assignable to parameter of type 'never'.
我们可以通过处理switch
内的'pending'
状态来修复此错误。
正如您从这个示例中看到的, assertNever
在switch
语句中特别有用,我们希望确保始终处理所有可能的情况。
nonNull
nonNull < T > ( value : T | null | undefined ) : value is T
value: T | null | undefined
是我们要检查是否为null
或undefined
值。 value is T
,这意味着该函数返回一个布尔值,指示传递的值是否既不是null
也不是undefined
。当返回true
时,这会将value
的类型缩小为T
(即排除null
和undefined
)。 nonNull
是一个谓词函数,用于检查指定值是否为非 null,即既不是null
也不是undefined
。调用该函数后,根据返回true
或false
适当缩小value
的类型。
每当我们有一个可能为null
、 undefined
或两者的值并且我们想要检查它并适当缩小其类型时,就应该使用nonNull
。
此函数的名称源自NonNullable
实用程序类型,该类型从类型中排除null
和undefined
。
declare const names : ( string | null ) [ ] ;
const nonNullNames = names . filter ( nonNull ) ;
// nonNullNames has type 'string[]'
在此示例中,我们有一个名称数组,其中还可以包含一些null
元素。我们过滤数组中的所有非空元素并返回string[]
。
如果我们改用names.filter(x => x !== null)
,我们会返回非 null 元素,但类型仍然是(string | null)[]
因为 TypeScript 会看到我们传递给filter
函数因为只是返回一个boolean
,所以不会发生类型缩小。
我们建议使用babel-plugin-dev-expression
来剥离传递给invariant
message
参数,并完全删除生产中对warning
的调用。
基本上,在生产中, babel-plugin-dev-expression
取代了
invariant ( value , 'Value is falsy!' ) ;
和
if ( ! value ) {
invariant ( false ) ;
}
所以message
参数被删除。它还完全消除了对warning
调用。所以,像这样的行
warning ( value , 'Value is falsy!' ) ;
被删除。
非常欢迎请求请求。如果您打算引入重大更改,请先打开相关问题,我们可以在其中讨论您想要更改的内容。
请确保适当更新测试和自述文件。
麻省理工学院