Security News
tea.xyz Spam Plagues npm and RubyGems Package Registries
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
babel-plugin-runtyper
Advanced tools
Changelog
0.4.0 (Jun 6, 2018)
forbiddenNodeEnvs
to suppress warning when NODE_ENV=production
([#12])Readme
Protect your App from type-coercion bugs
Runtyper is a Babel plugin for runtime type-checking in JavaScript. You should enable it for non-production build and check console for type-coercion warnings. As it works in runtime - no manual type-annotations needed in your codebase.
Imagine you have comparison x === y
and in runtime values are x = 1
, y = "1"
.
When executed you will get false
. In many cases this result is unexpected: you just missed type conversion.
After applying Runtyper it will show warning when such situation happen:
or you can configure to throw errors:
Runtyper wraps all type-important operations into function.
When line is executed, function checks the argument types first and only then returns the result.
For example, before:
if (x === y) { ... }
After (simplified):
if (strictEqual(x, y)) { ... }
function strictEqual(a, b) {
if (typeof a !== typeof b) {
console.warn('Strict compare of different types: ' + typeof a + ' === ' + typeof b);
}
return a === b;
}
npm install babel-plugin-runtyper --save-dev
No changes to your existing codebase needed.
Add babel-plugin-runtyper
to Babel config:
in .babelrc
:
{
"plugins": ["babel-plugin-runtyper"]
}
To apply plugin only for development builds consider Babel's env option:
{
"env": {
"development": {
"plugins": ["babel-plugin-runtyper"]
}
}
}
in webpack config:
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
plugins: [
['babel-plugin-runtyper', {enabled: process.env.NODE_ENV !== 'production'}]
]
}
}
}
]
}
Please note to run webpack as
NODE_ENV='production' webpack -p
(see #2537)
in package.json
scripts:
"scripts": {
"runtyper": "babel src --out-dir out --plugins=babel-plugin-runtyper --source-maps"
}
Enable source-maps to see original place of error:
Enable JavaScript source maps
in devtools settingsrequire('source-map-support').install();
Tip: checkout examples directory to see browser and Node.js demos
To configure plugin pass it to Babel as array:
plugins: [
['babel-plugin-runtyper', options]
]
Name | Default | Values | Description |
---|---|---|---|
enabled | true | true , false | Is plugin enabled |
warnLevel | "warn" | "info" , "warn" , "error" , "break" | How do you want to be notified |
implicitAddStringNumber | "deny" | "allow" , "deny" | Allow/deny (variable1) + (variable2) where (variable1), (variable1) are (string, number) |
implicitEqualNull | "deny" | "allow" , "deny" | Allow/deny (variable1) === (variable2) where (variable1) or (variable2) is null |
implicitEqualUndefined | "deny" | "allow" , "deny" | Allow/deny (variable1) === (variable2) where (variable1) or (variable2) is undefined |
explicitAddEmptyString | "deny" | "allow" , "deny" | Allow/deny (variable) + "" where (variable) is not string |
explicitEqualTrue | "deny" | "allow" , "deny" | Allow/deny (variable) === true where (variable) is not boolean |
explicitEqualFalse | "deny" | "allow" , "deny" | Allow/deny (variable) === false where (variable) is not boolean |
implicitEqualCustomTypes | "deny" | "allow" , "deny" | Allow/deny (variable1) === (variable2) where (variable1) instanceof MyClass1 and (variable2) instanceof MyClass2 |
excludeOperators | [] | ["equal", "numeric", "add", "relational"] | Excludes operators checking where equal excludes === , numeric excludes -, *, /, % , add excludes + and relational excludes >, >=, <, <= |
forbiddenNodeEnvs | ["production"] | Array<String> | Values of NODE_ENV where plugin shows warning if enabled |
Warning level description
info
- notification via console.info
without stacktracewarn
- notification via console.warn
with stacktraceerror
- notification via console.error
with stacktracebreak
- notification via throwing error and breaking executionBy default configuration is very strict. You can start with the softest one:
{
enabled: true,
implicitAddStringNumber: "allow",
implicitEqualNull: "allow",
implicitEqualUndefined: "allow",
explicitAddEmptyString: "allow",
explicitEqualTrue: "allow",
explicitEqualFalse: "allow",
implicitEqualCustomTypes: "allow"
}
The result can be something like this:
Error: Strict equal of different types: -1 (number) === "" (string)
Error: Strict equal of different types: 2 (number) === "" (string)
Error: Strict equal of different types: 56.9364 (number) === "" (string)
Error: Strict equal of different types: -0.0869 (number) === "" (string)
Error: Numeric operation with non-numeric value: null / 60 (number)
Error: Numeric operation with non-numeric value: "2017-03-29T00:00:00... (Date) / 1000 (number)
Error: Numeric operation with non-numeric value: "2017-03-29T00:00:00... (Date) / 1000 (number)
...
Strict equality (===, !==
)
Protects you from:
1 === '1' // false
1 === [1] // false
1 === new Date() // false
...
Addition (+
)
Protects you from:
'1' + null // '1null'
'1' + undefined // '1undefined'
'1' + NaN // '1NaN'
1 + NaN // NaN
1 + {} // '1[object Object]'
...
Arithmetic (-, *, /, %
)
Protects you from:
1 - '1px' // NaN
1 - undefined // NaN
1 * null // 0
1 * {} // NaN
...
Relational (>, >=, <, <=
)
Protects you from:
2 < '11' // false (but '1' < '11' is true)
1 < null // false
[1] < {} // true
2 < [11, null] // false (but 2 < [11] is true)
...
You can exclude line from checking by special comment:
if (x === y) { // runtyper-disable-line
...
}
You can easily try Runtyper on existing project because no special code-annotations needed. Just build your project with Runtyper enabled and perform some actions in the app. Then inspect the console. You may see some warnings about type-mismatch operations:
Error: Strict equal of different types: -1 (number) === "" (string)
Error: Strict equal of different types: 2 (number) === "" (string)
Error: Strict equal of different types: 56.9364 (number) === "" (string)
Error: Strict equal of different types: -0.0869 (number) === "" (string)
Error: Numeric operation with non-numeric value: null / 60 (number)
Error: Numeric operation with non-numeric value: "2017-03-29T00:00:00... (Date) / 1000 (number)
Error: Numeric operation with non-numeric value: "2017-03-29T00:00:00... (Date) / 1000 (number)
...
Static code analysis also performs type checking. You can use Runtyper together with Flow or TypeScript to detect errors on both build and runtime stages.
Yet, static tools need extra efforts for:
To learn more about pros and cons of static types have a look on Eric Elliott's article You Might Not Need TypeScript (or Static Types).
Let's take an example from Flow's get started page:
// @flow
function square(n) {
return n * n; // Error!
}
square("2");
But if square()
is used to handle user's input from text field - error will not be found:
// @flow
function square(n) {
return n * n; // no Error, until you annotate `event.target.value`
}
window.document.getElementById('username').addEventListener('change', function (event) {
square(event.target.value);
});
Runtyper allows to catch such cases in runtime:
Consider both approaches to make your applications more robust and reliable.
Why I get error for template literals like ${name}${index}
?
Likely you are using babel-preset-es2015
that transforms template literals into concatenation +
.
And you get (string) + (number)
. You can fix it in several ways:
implicitAddStringNumber: "allow"
${name}${String(index)}
Why explicit comparing like x === null
or x === undefined
are not warned?
When you explicitly write (variable) === null
you assume that variable can be null
.
Does it check non-strict equal ==
and !=
?
Nope. Non-strict comparison is a bad thing in most cases. Just quote Douglas Crockford from JavaScript, the Good Parts:
JavaScript has two sets of equality operators:
===
and!==
, and their evil twins==
and!=
. The good ones work the way you would expect. If the two operands are of the same type and have the same value, then===
producestrue
and!==
producesfalse
. The evil twins do the right thing when the operands are of the same type, but if they are of different types, they attempt to coerce the values. The rules by which they do that are complicated and unmemorable.
These are some of the interesting cases:
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true
The lack of transitivity is alarming. My advice is to never use
==
and!=
. Instead, always use===
and!==
.
Explicit is always better when implicit, especially for readers of your code.
You can set ESLint eqeqeq rule and forget about ==
once and for all.
If you have other questions or ideas feel free to open new issue.
Thanks goes to these wonderful people (emoji key):
Vitaliy Potapov 💻 | Revelup Zilvinas Rudzionis 💻 |
---|
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT @ Vitaliy Potapov
FAQs
Babel plugin for runtime type-checking in JavaScript
The npm package babel-plugin-runtyper receives a total of 29 weekly downloads. As such, babel-plugin-runtyper popularity was classified as not popular.
We found that babel-plugin-runtyper demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
Security News
As cyber threats become more autonomous, AI-powered defenses are crucial for businesses to stay ahead of attackers who can exploit software vulnerabilities at scale.
Security News
UnitedHealth Group disclosed that the ransomware attack on Change Healthcare compromised protected health information for millions in the U.S., with estimated costs to the company expected to reach $1 billion.