Today I Learned

A Hashrocket project

182 posts about #javascript

Use Typescript to help migrate/upgrade code

I am tasked with migrating a large javascript codebase from using the beta version of firebase-functions to the latest. Like most major upgrades, there are many API changes to deal with. Here’s an example cloud function:

Beta version with node 6:

exports.dbCreate = functions.database.ref('/path').onCreate((event) => {
  const createdData = event.data.val(); // data that was created
});

Latest version with node 10:

exports.dbCreate = functions.database.ref('/path').onCreate((snap, context) => {
  const createdData = snap.val(); // data that was created
});

The parameters changed for onCreate.

In the real codebase there are hundreds of cloud functions like this and they all have varying API changes to be made. With no code-mod in sight, I’m on my own to figure out an effecient way to upgrade. Enter Typescript.

After upgrading the dependencies to the latest versions I added a tsconfig:

{
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "lib",
    "target": "es2017",
    "allowJs": true,
    "checkJs": true,
  },
  "include": ["src"]
}

The key is to enable checkJs so that the Typescript compiler reports errors in javascript files.

And running tsc --noEmit against the codebase provided me with a list of 200+ compiler errors pointing me to every change required.

Use assigned variables on its own assign statement

Javascript allows you to use an assigned variables into the same assignment statement. This is a non expected behavior for me as the first languages I’ve learned were compiled languages and that would fail compilation, at least on the ones I’ve experienced with.

Here’s an example:

Javascript ES2015:

const [hello, world] = [
  () => `Hello ${world()}`,
  () => "World",
]
hello()
//=> "Hello World"

In this example I’m assigning the second function into the world variable by using destructuring assignment and I’m using this by calling world() in the first function.

Ruby:

To my surprise ruby allows the same behavior:

hello, world = [
  lambda { "Hello #{world.call}" },
  lambda { "World" },
]
hello.call
#=> "Hello World"

Elixir:

Well, Elixir will act as I previously expected, so assigned variables are allowed to be used only after the statement that creates them.

[hello, world] = [
  fn -> "Hello #{world.()}" end,
  fn -> "World" end,
]
hello.()
#=> ERROR** (CompileError) iex:2: undefined function world/0

I won’t start using this approach, but it’s important to know that’s possible.

Javascript arguments on ES2015 Arrow functions

Javascript function arguments can be accessed by functions defined using the function keyword such as:

function logArgsES5 () {
  console.log(arguments)
}
logArgsES5('foo', 'bar')
// => Arguments(2) ["foo", "bar"]

But ES2015 Arrow functions does not bind this variable, so if you try this you will see an error:

let logArgsES2015 = () => {
  console.log(arguments)
}
logArgsES2015('foo', 'bar')
// => Uncaught ReferenceError: arguments is not defined

So if we want to have similar variable we can add an ...arguments as the function argument:

let logArgsES2015 = (...arguments) => {
  console.log(arguments)
}
logArgsES2015('foo', 'bar')
// => Array(2) ["foo", "bar"]

FormData doesn't iterate over disabled inputs

If I have a form that looks like this:

<form>
  <input disabled name="first_name" />
  <input name="last_name" />
</form>

And then I use FormData to iterate over the inputs.

const form = new FormData(formElement);

const fieldNames = [];

form.forEach((value, name) => {
  fieldNames.push(name);
});

// fieldNames contains ['last_name']

Then fieldNames only contains one name!

Be careful if you’re using FormData to extract names from a form!

Set Splits Strings

Examples of JavaScript’s Set usually include an array as the argument:

const arraySet = new Set([1, 2, 3, 4, 5]);

However, the Set constructor also accepts a string, splitting it into a set for you:

const text = 'India';

const stringSet = new Set(text);  // Set ['I', 'n', 'd', 'i', 'a']

Enjoy those extra keystrokes!

docs

Tuples are never inferred in TypeScript

If I have a function that takes a number tuple:

type Options = {
  aspect: [number, number],
  ...
}

const pickImage = (imageOptions: Options) => (
  ...
)

This will give a type error:

const myOptions = {
  aspect: [4, 3],
};

// ❌ Expected [number, number], got number[]
pickImage(myOptions);

I must use type assertion when passing tuples:

const myOptions = {
  aspect: [4, 3] as [number, number],
};

pickImage(myOptions);

Firefox Built-in JSON Tools ⛏

Recent versions of Firefox (I’m on 67.0.1) have built-in tools to explore JSON.

Here’s a screenshot of a JSON file from the React Native docs, viewed in Firefox:

image

We get syntax highlighting, the ability to save, copy, expand, or collapse the JSON, right in the browser, with no extra plugins or tools required. Another great Firefox feature!

Jest toEqual on js objects

jest toEqual compares recursively all properties of object with Object.is. And in javascript an object return undefined for missing keys:

const obj = {foo: "FOO"}

obj.foo
// "FOO"

obj.bar
// undefined

So it does not make sense to compare an object against another one that has a undefined as value, so remove them all from the right side of the expectation to avoid confusion. In other words, this test will fail:

test("test fails => objects are similar", () => {
  expect({
    planet: "Mars"
  }).not.toEqual({
    planet: "Mars",
    humans: undefined
  });
});

// Expected value to not equal:
//   {"humans": undefined, "planet": "Mars"}
// Received:
//   {"planet": "Mars"}

Watch out!

Readable code with objects, constants & attributes

Occasionally when working with external APIs, you need to send over some cryptic status:

postData(
  `http://example.com/orders/123`,
  { status: 2 }
)

But what does 2 mean?!

We can use objects to make this a bit easier to maintain

const STATUS = { processing: 0, error: 1, completed: 2 }

postData(
  `http://example.com/orders/123`,
  { status: STATUS.completed }
)

Now we can immediately see the order status is completed.


This technique is applicable in other languages as well:

Ruby with a constant:

STATUS = { error: 0, processing: 1, completed: 2 }
STATUS[:completed]

Elixir as a module attribute

defmodule Order do
  @status %{error: 0, processing: 1, completed: 2}

  def completed, do: @status.completed
end

Alphabetize Keys with jq

In a previous post, I wrote about how jq is great because it doesn’t alphabetize JSON keys by default. I still think that’s great, because sometimes the key order is meaningful, such as might be found in a package.json file.

We can add alphabetization to jq, however, using the -S flag. To format and and sort you current buffer in Vim, run the following:

:%!jq -S '.'

Happy JSON-ing!

Prevent npm high level security errors in CI

In npm 6.6, a feature was added to provide security audit information for the packages that are used in your application.

This is run with:

node audit

This exits with a non-zero exit code if any ‘low’, ‘medium’, ‘high’, or ‘critical’ errors were detected.

You can use that non-zero return code in your CI to fail a check, which should notify you of the security vulnerability which you can then resolve.

If you care about ‘high’ or ‘critical’ errors but don’t care about ‘low’ or ‘medium’ you can set the audit-level npm config value to ‘high’ in you npm configuration for your CI server.

Truncate an Array

Every time I go to truncate and array in JavaScript, I have to look up the syntax. It’s something that makes me angry.

Did you know that there is an oddly easier way to do it without reaching for splice() or slice()?

const collection = [4,6,9,1,12,42];
collection.length = 3;

collection is now [4,6,9]

Hopefully I can remember this one!

Get the return type of a function in TypeScript

Sometimes you only want to rely on the return type of a function instead of defining a new type for it.

To do that in TypeScript use ReturnType and typeof:

function extractStatusData(buffer: Buffer) {
  return {
    active: buffer[0] !== 1,
    totalErrors: buffer[1]
  }
}

function onStatusChange(callback: (data: ReturnType<typeof extractStatusData>) => void) {
  listenToSomeBinaryStuff(buffer => {
    const statusData = extractStatusData(buffer)
    callback(statusData)
  });
}


onStatusChange(({active, totalErrors}) => {
  if (active) {
    console.log(`Task is still running`)
  } else {
    console.log(`Task is completed with ${totalErrors} errors`)
  }
})

The paste event in browsers

Browsers allow you to capture a paste event in a DOM element [1][2].

This event fires before any clipboard data is inserted into the document, which makes it ideal for manipulating the data and pasting the manipulated data instead.

In Today I Learned by Hashrocket we recently utilized this feature to enable pasting images straight into the post editor. The image is then uploaded to imgur.com and the resulting URL is pasted as a Markdown Image Tag into the textbox.

image

If you are interested in adding similar functionality to your site check out this PR.

Also consider hosting your own fork of TIL.

Install node modules in subdir without a CD

So yea you don’t need a friend to send you a CD-ROM anymore to install node modules. You can use the World Wide Web! Pretty exciting.

On a more serious note, if your front-end is stored in a sub directory of your project, for example:

❯ tree -d -L 2
.
├── assets
|   ├── package.json
|   └── package-lock.json
└── app

Normally you would cd into assets, run npm i, and cd back out to the project root so you can go back to doing important things:

cd assets
npm i
cd -

Or you can push and pop, cause you’re kinda smart

pushd assets && npm i && popd

Or if you’re a really cool kid you’d use a sub-shell

(cd assets && npm i)

But wait! There’s another option - use the prefix CLI option in NPM!

npm i --prefix assets

It works really well for those long Dockerfile RUN statements.

for...in Iterates Over Object Properties

I don’t reach for for loops very often, so when I needed one recently I thought I’d check out the newer for...in construct. It didn’t behave quite how I was expecting. I thought it would iterate over the values in the target list, instead it seemed to be iterating over the indices.

The MDN docs explain what is going on:

The for...in statement iterates over all non-Symbol, enumerable properties of an object.

An array is an object whose properties are the indices of the values stored in the array.

const fruits = ["apple", "banana", "orange"];
for (let fruit in fruits) {
  console.log(fruit, fruits[fruit]);
}
// => "0" "apple"
// => "1" "banana"
// => "2" "orange"

The iteration value wasn’t what I was looking for, but in this case I can use it to access the value from the list. I’d be better off using a standard for loop though.

Imports in ES5

The ES6 import keyword is so pervasive in how we write JavaScript currently that when when I had to “import” the path package in ES5 I didn’t know how to do it!

It’s really easy though, and if you don’t like magic keywords maybe it’s a little more intuitive too.

const path = require('path');

And if something is the default export in it’s module, then you can use the default property.

const Something = require('something').default;

I ran into this in a file that was outside the build path, gatsby-config.js.

Destructured Access To Nested Value & Parent Value

A destructuring pattern that I often see (especially in React code) is to peel off a nested value in the argument declaration of a function.

const Component = ({ data: { name: displayName }}) => {
  return (
    <div>
      <h1>{displayName}</h1>
      <SubComponent />
    </div>
  );
};

On its own this works quite well, but what happens when you need access to the full set of data as well as the nested name value? I often see this.

const Component = ({ data }) => {
  const { name: displayName } = data;
  return (
    <div>
      <h1>{displayName}</h1>
      <SubComponent data={data} />
    </div>
  );
};

ES6 destructuring is flexible. You can skip the const line and keep everything in the argument declaration.

const Component = ({ data: { name: displayName }, data }) => {
  return (
    <div>
      <h1>{displayName}</h1>
      <SubComponent data={data} />
    </div>
  );
};

You can re-reference the data value after the nested destructuring.

Matching A Computed Property In Function Args

The computed property name feature of ES6 allows you to reference a variable in object assignments and destructurings. This syntax is flexible enough that it can be used in the arguments portion of a function declaration. In fact, it can even be matched against another argument — allowing the creation of some handy, yet terse functions.

const get = (key, { [key]: foundValue }) => foundValue;

Notice that the first argument, key, will match against the computed property name in the second argument. The foundValue will correspond to whatever key maps to in the given object.

This get function can then be used like so.

const stuff = { a: 1, b: 2, c: 3 };

console.log("Get a:", get("a", stuff)); // Get a: 1
console.log("Get d:", get("d", stuff)); // Get d: undefined

h/t @sharifsbeat

Javascript Default Named Parameters

I wanted to create a function with some named parameters as optional values, but I wanted them to have a default value for each option. I could use function({max = 10, min = -10}){} but this only works if you pass a js object as argument. So I added a default object into that and it just works great. Check this out:

avg = function({max = 10, min = -10} = {max: 11, min: -11}) {
  let avg = (max + min) / 2;
  console.log('min', min, 'max', max, 'avg', avg);
  return avg;
}

avg();
// => min -11 max 11 avg 0

avg({});
// => min -10 max 10 avg 0

avg({max: 5});
// => min -10 max 5 avg -2.5

avg({min: 6});
// => min 6 max 10 avg 8

avg({max: 5, min: 6});
// => min 6 max 5 avg 5.5

Default values are different on this example for illustration purposes.

Check The Password Confirmation With Yup

The Yup library makes it easy to validate individual values in a JavaScript object. A common situation when implementing a Sign Up form is asking the user to input their password twice and then the app can make sure they match.

To do this, we need the validation of our passwordConfirmation value to reach outside of itself to make a comparison with the password value. This can be done with Yup’s ref function.

import * as Yup from 'yup';

validationSchema: Yup.object({
  password: Yup.string().required('Password is required'),
  passwordConfirmation: Yup.string()
     .oneOf([Yup.ref('password'), null], 'Passwords must match')
});

We are able to reference the value of password with ref. We use the oneOf function to ensure that passwordConfirmation either matches password or if it is left blank and matches null then it passes the validation for the time being. The second argument to oneOf is a custom validation message when this validation fails.

source

Easy Date Comparison With DayJS

Let’s say my application fetches dates from the server which come back in string form as "YYYY-MM-DD" and I’d like to know if those dates already passed. This can be done easily by wrapping dates in DayJS and using its comparison functions.

import dayjs from 'dayjs';

const today = dayjs(new Date());
const pastDate = dayjs("2018-10-22");
const futureDate = dayjs("2022-01-01");

console.log(pastDate.isBefore(today));
// => true
console.log(futureDate.isBefore(today));
// => false

The dayjs() function can be used to construct DayJS date objects from Date objects and strings. These can then be compared with functions like isBefore() and isAfter().

ES6 Nested Destructuring

I picked this up a while ago on Twitter, use it all the time in my React code, and wanted to document it here. If you’re destructuring a JS object like this:

const { username, zip } = props.user;

A valid (and slightly better, in my opinion) refactor is this:

const { user : { username, zip } } = props;

As you add more destructured variables pulled from props, the object you’re referencing stays the same.

Note that this doesn’t define user— if that’s something you want, you have to destructure it on its own:

const { user, user : { username, zip } } = props;

While a little more verbose, I still think this makes for more maintainable destructuring long-term.

Get The Time Zone Of The Client Computer

The resolvedOptions function on Intl.DateTimeFormat.prototype provides a number of pieces of information about the client computer. It includes information such as the locale and the numbering system. It also has the time zone for that machine.

Try running this line of JavaScript in your own browser.

$ Intl.DateTimeFormat().resolvedOptions().timeZone

When I run it, I get America/Chicago.

You can use this within your client-side code as a way of determining in which time zone your users are.

Ignore just one statement with Prettier

Prettier works great for formatting your javascript. In general, it makes formatting something that you don’t have to think about anymore, but occasionally you’ll run across something that you want to format in your own custom way.

You can include a prettier-ignore comment to achieve this goal.

// prettier-ignore
const minesweeperMap = [
    'B', ' ', ' ',
  ' ', 'B', ' ',
  'B', ' ', ' ',
  ' ', ' ', 'B',
];

const bombs =     4;

When you run prettier on the above code, the declaration of bombs line will be changed to remove unneeded spaces, but the minesweeperMap declaration will be left unchanged.

Notify your application when browser goes offline

At JS Camp this past weekend I saw Josh Beckman give a talk about the appropriate time to trigger a page refresh when the single page application has received some updates.

A key component of that is knowing whether you have an internet connection or not.

Fortunately there is a browser event, offline, that will help you keep track of your offline status.

window.addEventListener('offline', () => console.log('is offline'))

When I turn the wifi off, then is offline is logged to the console.

You can also take advantage of the navigator.onLine (yes a capital L) property to determine whether your application currently has an internet connection.

Rendering Emojis With Unicodes in Javascript

Ordinarily you can render an emoji in Javascript by using its four-character Unicode code point as follows:

(React JS [JSX] Sample)

<span>{'\u263B'}</span> => <span>☻</span>

Sometimes, however, the Unicode code point for an emoji is more than four characters long and the above method for rendering doesn’t work. In ECMA6, the following syntax will work for emojis whose Unicode code point length is more than four (1):

<span>{'\u{1F4A9}'}</span> => <span>💩</span>

Note:

  1. This has only been tested with a five-character Unicode code point.

Speed up webpacker by excluding dev dependencies

In our latest project, we were experiencing some long build times after accreting features for a few monhts.

By default webpacker pulls in all of your node_modules for parsing and optimization by babel, which can be quite unneccessary. In order to exclude your devDependencies from that process, you can add the folling code to your development.js config:

...

var package = require('../../package.json');
var excluded = Object.keys(package.devDependencies).map(function(dep){
  return new RegExp("node_modules/" + dep);
});

module.exports = merge(environment.toWebpackConfig(), customConfig, {
  module: {
    noParse: excluded
  }
});

The noParse option can possibly lead to errors when some packages are excluded from parsing (notably, css-loader), you can tweak which dependencies reside in dependencies vs devDependencies in your package.json in order to avoid these issues.

Pass arguments through to wrapped function

If you want to have a function that wraps another function it’s convenient to not worry about the details of the arguments being passed.

I have this:

sendMessage = (name, num) => () {
  console.log(name, num);
}

And I want to wrap it with this:

wrapper = (func, name, num) => () {
  func(name, num);
}

Not all functions I want to wrap have the arguments name and num. To genericize this I can use ...args in the signature to turn all the remaining args into an array and then use ...args again to spread the array into the argument positions of the function call.

wrapper = (func, ...args) => () {
  func(...args);
}

To have the same operator (...) do opposite things based on the context is a little bit weird to me, but that’s ES6!

Custom Cypress Commands

I’ve been loving my first forays into Cypress integration testing. It feels really well made.

Today I learned how to create a custom Cypress command. Logging into the application is a classic use-case: I need to do it in almost every test, and I’d like a function that accepts arguments and centralizes the DOM manipulation.

Here it is:

// cypress/support/commands.js

Cypress.Commands.add('signIn', ({ email, password }) => {
  cy.visit('/sign-in');
  cy.get('input[name="email"]').type(email);
  cy.get('input[name="password"]').type(password);
  cy.get('form').submit();
});

The command is now available in any test without an explicit import:

cy.signIn({ email: 'dev@hashrocket.com', password: 'password' });

Abstraction!

docs

Assigning, Mutating, and Freezing a JS object

I’ve been using Object.assign to merge two objects in javascript. That typically looks like this:

> let o = Object.assign({a: 1, b: 2}, {a: 3})
{a: 3, b: 2}

The key/values in the second argument override the key/values in the first object to produce a new object with those the combined keys and values.

Does it actually produce a new object? or does it just mutate the first argument:

> foo = {a: 1, b: 2}
> bar = Object.assign(foo, {a: 3})
> foo == bar
true
> foo
{a: 3, b: 2}

Ahh… it mutates. If you prefer something not to mutate you can freeze it:

> foo = {a: 1}
> bar = Object.freeze(foo)
> Object.assign(bar, {a: 2})
TypeError: Cannot assign to read only property 'a' of object '#<Object>'

Ok, so now Object.assign can’t mutate the first argument, because that argument is frozen. But be careful, freeze mutates too.

> Object.assign(foo, {a: 2})
TypeError: Cannot assign to read only property 'a' of object '#<Object>'

`classList.toggle()` 2nd arg doesn't work in IE11

Months and months ago I faced a situation where I had to remove an element’s class outside of the react render tree.

Because toggle was being used to add the class in the code previously I used to toggle to remove it, and to ensure that it was removed and not added I used the second argument which would force it to be removed.

Today I learned that the second argument does not work in IE11.

In general, using toggle is an anti-pattern because it’s non deterministic, it depends on state that is unavailable to you when writing the code.

There are methods add and remove for classList, they have some IE11 edges too, but are more explicit about their intention.

See more info here.

Limiting Regex Alternation

The pipe operator (|, or the ‘alternation’ operator) is the ‘or’ of Regexes. Here’s a JavaScript example:

> 'hello'.match(/^hello|goodbye$/)
[ 'hello', index: 0, input: 'hello' ]
> 'goodbye'.match(/^hello|goodbye$/)
[ 'goodbye', index: 0, input: 'goodbye' ]

You might think this Regex can be true in two situations: the string is 'hello' or 'goodbye', because the Regex includes the start-of-line and end-of-line characters (^ and $). I have bad news:

> 'hello there'.match(/^hello|goodbye$/)
[ 'hello', index: 0, input: 'hello there' ]

What’s going on here? This is truthy because the | has a low precedence. It’s evaluated last, after other characters like () and ?. To put this Regex into words (I think): “does this string match anything before the pipe (including the start of line character), or anything after the pipe (including the end of line character)?”. The expression matches on ^hello and ignores anything after that.

We can contain it by telling the pipe to stop evaluating. Parentheses work because they have a higher order of precedence. Here’s our new Regex:

> 'hello there'.match(/^(hello|goodbye)$/)
null
> 'say goodbye'.match(/^(hello|goodbye)$/)
null

Bottom line: when using the pipe the way I’ve described, use parentheses.

NODE_OPTIONS without node

I was using storybook and found it had grown so large that its node process would run out of memory before it could start. The downside was that the storybook command did not accept node arguments, like --max_old_space_size=4096

Starting with node 8, node will look for options in an environment variable named NODE_OPTIONS to be interpreted as if they had been specified on the command line before the actual command line (so they can be overridden).

Example to give storybook more memory:

NODE_OPTIONS=--max_old_space_size=4096 storybook start

*References:
https://storybook.js.org
https://nodejs.org/dist/latest-v8.x/docs/api/cli.html#cli_node_options_options

Create A Cancelable Promise With PCancelable

If an async task takes a really long time and we find out in the middle of its execution that we no longer want the results of that activity, it would be nice to be able to cancel that promise. There was a proposal for cancelable promises, but it has since been withdrawn. There is an alternative though.

The p-cancelable package provides a promise wrapper that acts as a cancelable promise.

import PCancelable from 'p-cancelable';

const fetchPromise = new PCancelable((resolve, reject, onCancel) => {
  setTimeout(() => {
    resolve({ ok: true, data: [1, 2, 3] });
  }, 10000);

  onCancel(() => {
    console.log('Promise is being canceled');
  });
});

fetchPromise.then(response => {
  console.log('Promise Resolved: ', response.data);
}).catch(err => {
  console.log('Promise Rejected: ', err);
});

fetchPromise.cancel();

We can create a promise in a familiar manner. We get an additional argument — the onCancel function. This is a function that will be executed if cancel is called before the promises has resolved or rejected. In the above example, the fetchPromise.cancel() line will be invoked before the setTimeout resolves. The promise will be canceled, causing a rejection which will push us into the catch handler.

Interval Comparison in JavaScript

Today I learned a bit about interval comparison. It’s something you might recall from grade-school math:

1 < 5 < 10 -- is this true?

Two languages I’ve found that support it are Python and Clojure:

> if (1 < 5 < 10):
...   True
...
True
> (< 1 5 10)
=> true

JavaScript doesn’t support interval comparison, so we have to find another way to check that condition. Here’s one technique:

if (1 < number && number < 10) {
  // do something
}

It’s a wordier that the previous examples, but I think keeping the variable in the middle of the condition stays true to the interval comparison idea.

Create An Object With No Properties

When you call new Object or even just instantiate an object with {}, you are creating an object that uses the Object prototype. This means it inherits from Object.prototype.

You can deliberately create an object with no properties by making sure that it does not inherit Object.prototype.

> const propertylessObject = Object.create(null);
{}

> propertylessObject.__proto__
undefined

Unlike most objects that we encounter as we write JavaScript, this object we created with Object.create(null) has no properties including no __proto__.

See Object.create and Object.prototype for more details.

Turn Off Console Error Messages In A Test

I’m using Jest to test a React component that requires a prop via PropTypes. In one of my tests, I want to test that component when the required prop is excluded. The side effect of doing this is that my test output gets cluttered with the PropType warning.

The thing to do is silence the error message during that test.

it('renders a component without a required prop', () => {
  const originalError = console.error;
  console.error = jest.fn();

  // test code here
  expect(shallow(<My Component />)).toDoSomething;

  console.error = originalError;
});

We can silence console.error by temporarily replacing it with a Jest-mocked function and then putting it back at the end of the test.