circumspect
— это набор функций, которые сделают ваш код TypeScript/JavaScript более безопасным. К этим функциям относятся invariant
, warning
, assertNever
и другие.
Существует множество полезных функций, которые могут сделать ваш код более безопасным, например, invariant
и assertNever
. Многие из этих функций доступны в виде отдельных пакетов npm (например, invariant
). Но устанавливать новый пакет (и в большинстве случаев аналог @types
) для каждой функции не очень удобно. Кроме того, эти пакеты обычно экспортируют свои функции как экспорт по умолчанию, из-за чего автоматический импорт VSCode с ними не работает.
Эта библиотека объединяет функции, которые делают ваш код более безопасным, в одном месте. circumspect
— это единственная зависимость, в которой есть все. А автоматический импорт VSCode работает так, как и ожидалось, поскольку все функции, предоставляемые circumspect
называются экспортом.
Более того, circumspect
на 100% написана на TypeScript, поэтому по умолчанию каждая функция правильно типизирована. Таким образом, нет необходимости устанавливать отдельные пакеты @types
.
Использование пряжи:
yarn add circumspect
Использование НПМ:
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
(только в разработке). Эта функция правильно сужает тип value
, исключая все ложные значения (например, null
и undefined
).
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
функция ничего не возвращает. warning
выдает предупреждение на консоль с данным message
если указанное value
является ложным. Предупреждение выдается только в разработке. В производстве эта функция ничего не делает.
Его следует использовать всякий раз, когда мы хотим выдать предупреждение только для разработчиков, если какое-то значение является ложным, что должно помочь разработчикам исправить некритические проблемы, о которых сообщается в предупреждающем сообщении.
Поскольку 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
были исчерпаны, то при вызове assertNever
со value
ошибка компилятора не возникает, поскольку в этот момент value
считается типом never
, а во время выполнения мы никогда не достигаем точки вызова assertNever
, а это означает, что никакой ошибки не будет.
Однако, если не все варианты объединения были исчерпаны, мы вызываем assertNever
с чем-то отличным от never
, и поэтому будет ошибка компилятора, говорящая что-то вроде
Argument of type 'x' is not assignable to parameter of type 'never'.
что мы можем исправить, обработав недостающие варианты. Дополнительную информацию о проверке полноты объединения и assertNever
можно прочитать в документации TypeScript.
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'.
Мы можем исправить эту ошибку, обработав состояние 'pending'
внутри switch
.
Как вы можете видеть из этого примера, assertNever
особенно полезен в операторах switch
, где мы хотим гарантировать, что все возможные случаи всегда обрабатываются.
nonNull
nonNull < T > ( value : T | null | undefined ) : value is T
value: T | null | undefined
— это значение, которое мы хотим проверить на предмет того, не является ли оно null
или undefined
. value is T
, что означает, что функция возвращает логическое значение, указывающее, является ли переданное значение ни null
, ни undefined
. Это сужает тип value
до T
(т. е. исключает null
и undefined
), когда возвращается true
. nonNull
— это функция-предикат, которая проверяет, является ли указанное значение отличным от NULL, т. е. не является ли оно ни null
, ни undefined
. После вызова функции тип value
корректно сужается в зависимости от того, было ли возвращено true
или false
.
nonNull
следует использовать всякий раз, когда у нас есть значение, которое потенциально может быть null
, undefined
или и то, и другое, и мы хотим проверить это и правильно сузить его тип.
Название этой функции происходит от служебного типа NonNullable
, который исключает из типа значения null
и undefined
.
declare const names : ( string | null ) [ ] ;
const nonNullNames = names . filter ( nonNull ) ;
// nonNullNames has type 'string[]'
В этом примере у нас есть массив имен, который также может включать в себя некоторые null
элементы. Мы фильтруем массив для всех ненулевых элементов и возвращаем string[]
.
Если бы вместо этого мы использовали names.filter(x => x !== null)
, мы бы получили ненулевые элементы, но тип все равно был бы (string | null)[]
поскольку TypeScript видит функцию, которую мы передаем для filter
как просто возвращает boolean
, и поэтому сужения типа не происходит.
Мы рекомендуем использовать babel-plugin-dev-expression
чтобы удалить аргумент message
, переданный в invariant
, и полностью удалить вызовы warning
в рабочей среде.
По сути, в рабочей среде babel-plugin-dev-expression
заменяет
invariant ( value , 'Value is falsy!' ) ;
с
if ( ! value ) {
invariant ( false ) ;
}
поэтому аргумент message
удаляется. Он также полностью удаляет вызовы warning
. Итак, такие строки
warning ( value , 'Value is falsy!' ) ;
удаляются.
Запросы на вытягивание очень приветствуются. Если вы намерены внести серьезные изменения, сначала откройте соответствующий выпуск, в котором мы сможем обсудить, что вы хотели бы изменить.
Обязательно обновите тесты и README соответствующим образом.
Массачусетский технологический институт