Today I Learned

A Hashrocket project

Vim Tags in Visual Mode 🏷

This is my 400th TIL! 🎉

I’ll file this under ‘Vim is endlessly composable’. Today I learned that Vim tags can be used to define a range in visual mode. Here’s how you’d fold your code between two Vim tags.

Go to the first tag. If you marked it 1, here’s how you’d do that:


Enter visual mode and extend to your second tag 2:


Enter command mode and fold the range:


Which automatically extends to:


I use this in big markdown files to hide all but the thing I’m currently working on. Enjoy.

Vim Reverse Sort

I use Vim’s :sort all the time. If I can’t have a more meaningful algorithm than alphabetical, at least my lists are in some kind of order.

This function comes in surprisingly useful while writing GitHub-style checkbox lists.

- [x] Check mail
- [ ] Play guitar
- [x] Write TIL

Sorting this list alphabeticaly puts the undone items at the top.

- [ ] Play guitar
- [x] Check mail
- [x] Write TIL

Reverse the order (in classic Vim style) with a bang:


GitHub PR Team Reviews

Pull request openers on GitHub can request a review from a GitHub user, or a team that has at least read-access to the repository. In the screenshot below, deploying the ‘Ninjas’ to review this PR is effectively the same as requesting a review from each ninja on the team. If they have notifications for such things, they’ll be notified, and they’ll see a banner on the PR’s show page requesting their input on the change.


This is a handy way to request a lot of reviews at once.

About Pull Request Reviews

Convert .mov to .gif with ffmpeg

Sometimes putting a gif in a pull request is really helpful for reviewers. If you’ve recorded a movie on an iOS simulator with xcrun simctl or just QuickTime, it’s very simple to convert them to animated .gifs


ffmpeg -i \
       -s 415x925 \
       -pix_fmt rgb24 \
       -r 10 \
       -f gif \

Now it would be nice to have a function that could extract the video dimensions automatically 🤔 mdls 🤯

1 function gif-mov() {
2   movie=$1
3   height=$(mdls -name kMDItemPixelHeight ${movie} | grep -o '[0-]\+')
4   width=$(mdls -name kMDItemPixelWidth ${movie} | grep -o '[0-9]\+')
5   dimensions="${width}x${height}"
6   ffmpeg -i ${movie} -s ${dimensions} -pix_fmt rgb24 -r 10 -f gif ${movie}.gif
7 }


$ gif-mov ~/Desktop/

Install all versions in .tool-versions with asdf

If you get the code for a new project and it is a project where versions are managed by asdf, then you will have a .tool-versions file and it will look something like this:

elixir 1.7.4-otp-21
erlang 21.3.8

If I don’t have those versions installed, then generally I install those individually.

If your working directory is the same version as the .tool-versions file then you can install all versions specified in that file with:

asdf install

Switch branches in git with... `git switch`

It’s experimental. It’s intuitive. It’s in the newest version of git, version 2.23.0. It is:

git switch my-branch

And, with every git command there are many ways to use the command with many flags:

You might want to create a branch and switch to it:

git switch -c new-branch

You might want to switch to a new version of a local branch that tracks a remote branch:

git switch -t origin/remote-branch

You can throw away unstaged changes with switching by using -f.

git switch -f other-branch

I feel that if I were learning git from scratch today, this would be much easier to learn, there’s just so much going on with checkout.

Delete remote branches with confirmation

Branches on the git server can sometimes get out of control. Here’s a sane way to clean up those remote branches that offers a nice confirmation before deletion, so that you don’t delete something you don’t want to delete.

git branch -a | grep remotes | awk '{gsub(/remotes\/origin\//, ""); print;}' | xargs -I % -p git push origin :%

The -p flag of xargs provides the confirmation.

Pretty-Print JSON in NeoVim/Vim using jq

I’ve written here before about how to pretty-print JSON in Vim but since then I have found an even easier method using jq.

jq is an amazing command line utility for processing, querying and formatting JSON. I use it all the time when I get a response from an API request and I want to extract information or simply to pretty-print it with colors. All you have to do is pipe the curl results into jq:

curl | jq


You can also use jq inside of NeoVim to pretty print a JSON string, right in your buffer using this command:



Mechanical Keyboard DIP Switches

I’ve owned a WASD keyboard for a while but totally forgot about the DIP switches on the bottom. What’s a DIP switch? From Wikipedia:

A DIP switch is a manual electric switch that is packaged with others in a group in a standard dual in-line package (DIP). The term may refer to each individual switch, or to the unit as a whole. This type of switch is designed to be used on a printed circuit board along with other electronic components and is commonly used to customize the behavior of an electronic device for specific situations.

DIP switch

On a WASD V2 87-key, these switches let you enable Mac mode (switch command and option), swap caps lock with CTRL, activate function commands, and more. These can be set in the OS or via different applications, but setting them at the hardware level is very convenient.

DIP Switch

Worktrees in Git

Ever want to have multiple branches of the same repository checked out at the same time? One option is to clone the repository multiple times. But Git worktrees allow multiple working trees to be attached to the same repository. This can be simpler to use and save storage space.

From inside an existing repository run the following to create a new worktree:

git worktree add /path/to/new/checkout other-branch

other-branch is now checked out at /path/to/new/checkout. You can work in that directory just as you can the original.

To remove the worktree when you are done:

git worktree remove /path/to/new/checkout

Get a writable temporary directory in Elixir

When you need to write a temporary file to disk it is common to assume a particular path exists based on the operating system the app is running on:

tmp_file = Path.join("/tmp", filename)

However depending on the configuration of the app as it is deployed in production, it may not have access to the /tmp directory. Fortunately Elixir provides a function that returns a writable path for temporary files:

dir = System.tmp_dir!()
tmp_file = Path.join(dir, filename)

From the docs (h System.tmp_dir):

Writable temporary directory.

Returns a writable temporary directory. Searches for directories in the following order:

    1. the directory named by the TMPDIR environment variable
    2. the directory named by the TEMP environment variable
    3. the directory named by the TMP environment variable
    4. C:\TMP on Windows or /tmp on Unix
    5. as a last resort, the current working directory

Returns nil if none of the above are writable.

I prefer to use the “bang” version System.tmp_dir! described as:

Same as tmp_dir/0 but raises RuntimeError instead of returning nil if no temp dir is set.

On my machine it returns something like this:


🔒 Serve Phoenix App Locally with HTTPS 🔒

The Phoenix Framework provides an easy mix task to automatically generate a self-signed SSL cert. This is useful if you want to test the app locally with HTTPS.

mix phx.gen.cert

The self-sigend certs will be stored in priv/cert so make sure you add that path to your .gitignore.

When finished the command will prompt you to update your endpoint configuration and a few imporant warnings:

If you have not already done so, please update your HTTPS Endpoint
configuration in config/dev.exs:

  config :tilex, TilexWeb.Endpoint,
    http: [port: 4000],
    https: [
      port: 4001,
      cipher_suite: :strong,
      certfile: "priv/cert/selfsigned.pem",
      keyfile: "priv/cert/selfsigned_key.pem"

WARNING: only use the generated certificate for testing in a closed network
environment, such as running a development server on `localhost`.
For production, staging, or testing servers on the public internet, obtain a
proper certificate, for example from [Let's Encrypt](

NOTE: when using Google Chrome, open chrome://flags/#allow-insecure-localhost
to enable the use of self-signed certificates on `localhost`.

Change Mechanical Keyboard Keycaps Faster ⌨️

I change the keys on my mechanical keyboards often. Sometimes I’m upgrading from stock, or trying an experiment, or just pursuing a look. It’s a reality of being really into keyboards.

Something I’ve learned along the way: removing all your old caps at once is a bad idea. When you do this, you have to figure out where every key goes. Some keycaps have a code on the underside telling you the row it belongs to, most do not. I end up pulling up my Mac virtual keyboard, or looking at another keyboard, to sort it out.

A better techique: take the new keys and line them up in the correct order for each row. Some keycaps ship in separate rows, making this easy. If that’s the case, slide each row out of its shrinkwrap like a sleeve of Thin Mints.

Then, change one row at a time. Remove all the keycaps in one row, and then put all the new ones in place. This is fast, foolproof, and if you get interrupted in between rows, you still have a functioning keyboard.


Tmux New Window and Process ⏩

This is a command I am continually a huge fan of. Here’s one way to open a new Tmux window, from Tmux command mode:

:new-window <program> <arguments>

My practical example from today:

:new-window psql my_database

This opens a new Tmux window psql with the arguments supplied, connecting me to my_database. When I terminate database connection, the window closes. For web development, this is a great way to quickly connect to a program, run some commands, then close the connection and cleanup the Tmux session.

Open the Vim Quickfix

Today I learned a new Vim command, :copen or :cope. The headline for this command is that it “open[s] a window to show the current list of errors”. The side benefit is that if you already have quickfix window in your buffers, like you would after greping the codebase, it will open or reopen that quickfix buffer.

See :help :copen for more info.

PostgreSQL index with NO lock on Rails

Rails allow us to create a PostgreSQL index that it would not lock the table for writing meanwhile it’s being calculated.

That’s usually handful if a table has millions of rows and this operation could take hours to release the INSERT/UPDATE/DELETE lock. Maybe the lock downtime is critical to the application.

There are caveats to this approach so please read the PostgreSQL documentation.

In order to do that you’ll need to add { algorithm: :concurrently } to the add_index on a Rails migration. Additionally PostgreSQL uses DB transactions to manage this new index creation so we need to disable the Rails migration transaction. Take this example:

class AddIndexOnEventsName < ActiveRecord::Migration[5.0]

  def change
    add_index(:events, :name, algorithm: :concurrently)

In this case I’m using disable_ddl_transaction! to disable the DB transaction that wraps each Rails migrations and I am using algorithm: :concurrently.

This option it will generate an index like that:

CREATE INDEX CONCURRENTLY "index_events_on_name" ON "events" ("name"));

Reset a Vim Split

When I make a Vim horizontal split, the two panes are evenly sized. I then often use :resize n to make one pane larger or smaller. To restore the panes to their evenly sized split, use <vim-leader> =. In the Hashrocket dotfiles, this translates to CTRL + W =.

Precise timings with `monotonic_time`

Monotonic time is time from a clock that only moves forward. The system clock on your CPU can be set and reset. Even when tied to the LAN ntp protocol the system clock can be out-of-sync by a couple of milliseconds. When measuring in microseconds, that’s a lot of time, and time drift can occur at the microsecond level even when attached to NTP, requiring system clock resets.

To get monotonic time in Elixir use, System.monotonic_time:

iex> System.monotonic_time
iex> System.monotonic_time

It’s ok that this number is negative, it’s always moving positive.

The number has a time unit of :native. To get a duration in millseconds you could convert from :native to millisecond.

iex> event_time = System.monotonic_time
iex> System.convert_time_unit(System.monotonic_time - event_time, :native, :millisecond)

Or you could get a millisecond duration by using the one argument of monotonic_time to specify the time unit you want.

iex> event_time = System.monotonic_time(:millisecond)
iex> System.monotonic_time(:millisecond) - event_time

Check out the elixir docs on time for more info.

Rails will change the `not` behavior

Yay Rails will change the behavior of the ActiveRecord#not on rails 6.1. That’s great as the current behavior sometimes leads to confusion. There will be a deprecation message on the version 6.0 so let’s watch out and change our code accordingly.

As I already wrote on this TIL this function does not act as a legit boolean algebra negation. Let’s say that we have this query:

User.where.not(active: true, admin: true).to_sql

Prior to this change this will produce this query:

SELECT users.*
  FROM users
 WHERE != 't'
   AND users.admin != 't'

The problem is that negating an AND clause is naturally a NAND operator, but Rails have implemented as a NOR. Some developers might wonder why regular active users are not returning.

On Rails 6.1 this will be fixed hence the new query will be:

SELECT users.*
  FROM users
 WHERE != 't'
    OR users.admin != 't'

Here’s the rollout plan:

  • Rails 5.2.3 acts as NOR
  • Rails 6.0.0 acts as NOR with a deprecation message
  • Rails 6.1.0 act as NAND

This is the PR in case you want to know more.

Optimize Images for the Web the Easy Way

TIL I learned about a really useful, open source, image optimization app called ImageOptim. This app will help you batch resize image files (png, jpg, gif) in either lossless or lossy way.


It is so much quicker than using an image editor such as Affinity Photo. And best of all it’s free!

It has helped me cut image sizes by as much as 94%! All without noticeable compression artifacts.

To install it on macOS simply run:

brew cask install imageoptim

For source and more info:



Javascript arguments on ES2015 Arrow functions

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

function logArgsES5 () {
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 = () => {
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) => {
logArgsES2015('foo', 'bar')
// => Array(2) ["foo", "bar"]

FormData doesn't iterate over disabled inputs

If I have a form that looks like this:

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

And then I use FormData to iterate over the inputs.

const form = new FormData(formElement);

const fieldNames = [];

form.forEach((value, name) => {

// fieldNames contains ['last_name']

Then fieldNames only contains one name!

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

React Testing Library => within nested queries

Wow, React Testing Library has a within helper to get nested searches on the dom. Check this out with this example:

const DATA = {
  movies: ["The Godfather", "Pulp Fiction"],
  tv: ["Friends", "Game of Thrones"],

const MyContainer = () => (
    {Object.keys(DATA).map(category => (
      <MyCategory name={category} list={DATA[category]} />

const MyCategory = ({name, list}) => (
  <ul data-testid={name} role="list">
    { => <li role="listitem">{item}</li>)}

Then let’s say if we want to assert the list of movies that this MyContainer renders and in the same order as it’s been rendered we can:

import { render, within } from "@testing-library/react";
import MyContainer from "../MyContainer";

describe("MyContainer", () => {
  it("tests movies", () => {
    const { getByTestId, getAllByRole } = render(<MyContainer />);

    const moviesCategory = getByTestId("movies");
    const movies = within(moviesCategory).getAllByRole("listitem");

    expect(items[0]).toHaveTextContent("The Godfather");
    expect(items[1]).toHaveTextContent("Pulp Fiction");

Real code is way more complex than this example, so this within helper function turns to be very convenient.

Erlang records are just tuples

Erlang has a Record type which helps you write clearer code by having data structures that are self descriptive.

One thing that is interesting about a Record is that a record is just a tuple underneath the hood.

Erlang/OTP 22 [erts-10.4.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Eshell V10.4.4  (abort with ^G)
1> rd(fruit, {name, color}).
2> #fruit{name="Apple", color="Red"}.
#fruit{name = "Apple",color = "Red"}
3> #fruit{name="Apple", color="Red"} == {fruit, "Apple", "Red"}.

As you can see, that internal representation is exposed when comparing a record to a tuple:

#fruit{name="Apple", color="Red"} == {fruit, "Apple", "Red"}.

You can even use pattern matching with the two data structures:

7> #fruit{name="Apple", color=NewColor} = {fruit, "Apple", "Green"}.
#fruit{name = "Aplle",color = "Green"}
8> NewColor.

Declaring Erlang records in a shell

Erlang records are typically declared in a header file. If you want to experiment with records at the command line, you’ll have to use a shell command.

rd is an erl shell command that you can remember as standing for record definition

Let’s try that in the erlang shell tool, erl.

Erlang/OTP 21 [erts-10.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

Eshell V10.1  (abort with ^G)
> rd(fruit, {name, color}).

And then you can declare a record the same way you would in Erlang.

> Apple = fruit#{name="Apple", color="Grren"}.
#fruit{name = "Apple",color = "green"}

And to inspect that variable in erl, just declare the variable and put a . after it to conclude the expression.

> Apple.
#fruit{name = "Apple",color = "green"}

You can find other shell commands that deal with records and other things here.

IO.inspect Label

When doing puts-driven-development in Elixir, IO.inspect/1 and IO.inspect/2 are very useful. These functions return their items unchanged, allowing us to spy on a value in our Elixir code.

I’m a fan of the :label option, which decorates our output with a label, making it easier to understand.

def channel_name_split(channel) do
  |> IO.inspect(label: "channel name before")
  |> String.split("")
  |> IO.inspect(label: "channel name after")

And here’s the output in our server log:

channel name before: "workflow"
channel name after: ["", "w", "o", "r", "k", "f", "l", "o", "w", ""]

Check out h IO.inspect in IEX for more info.

telemetry handler detaches automatically on error

Telemetry handlers should be designed to run efficiently over and over again without making a noticeable performance impact on your production system. But what happens if an occurs? Your monitoring should have no bearing on the success of your business logic.

So what happens if your monitoring has an error? With telemetry, it’s ok, the offending handler is just detached, never to run again. Business logic unaffected.

Here’s a test that demonstrates the handler being removed.

handlerFn = fn _, _, _, _ ->
  raise "something"


# the handler is in ets
assert [[[:something_happened]]] =
         :ets.match(:telemetry_handler_table, {:handler, :bad_handler, :"$1", :_, :_})

:telemetry.execute([:something_happened], %{}, %{})

# the handler is gone!
assert [] = :ets.match(:telemetry_handler_table, {:handler, :bad_handler, :"$1", :_, :_})

You can see the try/catch block in the telemetry source code here.

List all telemetry event handlers

Telemetry is a new library for application metrics and logging in beam applications. It was added to Phoenix in version 1.4.7 released in June 2019.

Telemetry consists of registered handler functions that are executed when specific events occur.

To see a list of what handlers are registered for which events you can call:


When returns a list of maps.

To see a list of handlers that have a specific event prefix, you can pass in a prefix as the only argument.

:telemetry.list_handlers([:phoenix, :endpoint])

Which returns:

    config: :ok,
    event_name: [:phoenix, :endpoint, :start],
    function: #Function<2.82557494/4 in Phoenix.Logger.install/0>,
    id: {Phoenix.Logger, [:phoenix, :endpoint, :start]}
    config: :ok,
    event_name: [:phoenix, :endpoint, :stop],
    function: #Function<3.82557494/4 in Phoenix.Logger.install/0>,
    id: {Phoenix.Logger, [:phoenix, :endpoint, :stop]}

`telemetry_event` Overrides Repo Query Event

Ecto gives you a single telemetry event out of the box, [:my_app, :repo, :query], where the [:my_app, :repo] is the telemetry prefix option for ecto.

This event is called whenever any request to the database is made:

  handler = fn _, measurements, _, _ ->
    send(self(), :test_message)

    [:test_telemetry, :repo, :query],



This event is overriden when using the the :telemetry_event option, a shared option for all Repo query functions.

  handler = fn _, measurements, _, _ ->
    send(self(), :test_message)

  custom_handler = fn _, measurements, _, _ ->
    send(self(), :custom_message)

    [:test_telemetry, :repo, :query],


  Repo.all(TestTelemetry.Colour, telemetry_event: [:custom])


Which means for any given query you can only broadcast one event. If you have a system that keeps track of expensive queries but you also need to debug a particular query in production, you will take that query out of the system to track expensive queries.

Proposing new time on Google Calendar invitations

Today I learned that we can respond to an invitation using google calendar proposing a new time. Check this out:

First when we open an invitation on gmail I can hit the link more options:


This will open the event on google calendar. Then we can expand more options with the caret down and hit Propose a new time.


Finally choose a time and send it back to the event owner. And that’s how they will receive our request to change the event time:


Telemetry Attach and Execute

The telemetry api is simpler than the name tends to imply. There are only two primary functions, attach/4 and execute/4. Check out the telemetry docs to see the full api.

Attach is simple. Essentially, you want a function to be called when a certain event occurs:

handler = fn [:a_certain_event], _measuremnts, _metadata, _config ->
  IO.puts("An event occurred!!")


The first argument is handler_id and can be anything be must be unique. The last argument is config and can also be anything. It will be passed along, untouched, to the handler function as the last argument.

Execute is simple. When the program calls execute, the handler that matches the event is called and the measurements and metadata are passed to the handler.

measurements = %{}
metadata = %{}
:telemetry.execute([:a_certain_event], measurements, metadata)

It’s important to note that the event name must be a list of atoms.

A simple test for attach and execute would look like this:

test "attach and execute" do
  handler = fn _, _, _, _ ->
    send(self(), :test_message)


  :telemetry.execute([:something_happened], %{}, %{})

  assert_receive :test_message

Where in with multiple values in postgres

Postgres has a record type that you can use with a comma seperated list of values inside of parenthesis like this:

> SELECT pg_typeof((1, 2));

(1 row)

What is also interesting is that you can compare records:

> select (1, 2) = (1, 2);

(1 row)

And additionally, a select statement results in a record:

> select (1, 2) = (select 1, 2);

(1 row)

What this allows you to do is to create a where statement where the expression can check to see that 2 or more values are contained in the results of a subquery:

> select true where (1, 2) in (
  select x, y
    generate_series(1, 2) x,
    generate_series(1, 2) y

(1 row)

This is useful when you declare composite keys for your tables.

Accumulating Attributes In Elixir

Typically, if you declare an attribute twice like this:

@unit_of_measure :fathom
@unit_of_measure :stone

The second declaration will override the first:

# :stone

But by registering the attribute by calling register_attribute you get the opportunity to set the attribute to accumulate. When accumulating, each declaration will push the declared value onto the head of a list.

defmodule TriColarian do
  @moduledoc false

  Module.register_attribute(__MODULE__, :colors, accumulate: true)

  @colors :green
  @colors :red
  @colors :yellow

  def colors do

# [:yellow, :red, :green]

At compile time, perhaps when executing a macro, you have the opportunity to dynamically build a list.

I learned this when Andrew Summers gave a talk on DSLs at Chicago Elixir this past Wednesday. You can see his slides here

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!


Keeping an SSH connection alive

Do you get disconnected from your SSH session often? I do… but I’ve found a solution that helps.

An SSH configuration that can be made on the server or client side but in my instance it makes more sense for the update to be on the client side.

In your ~/.ssh/config we’ll utilize the ServerAliveInterval declaration.

             Sets a timeout interval in seconds after which if no data has
             been received from the server, ssh(1) will send a message through
             the encrypted channel to request a response from the server.  The
             default is 0, indicating that these messages will not be sent to
             the server.  This option applies to protocol version 2 only.

This declaration informs the client to send a keep alive packet at a certain interval to the server, ensuring the connection stays open.

Host example
  ServerAliveInterval 30

That declaration will send a packet every 30 seconds if the connection goes idle. Now this is the desired funcationality that I’d like to see for all of my SSH connections so we can add it to the host wildcard like so:

Host *
  ServerAliveInterval 120

GitHub Insert a Suggestion ☝

Today I learned that GitHub has allows you to insert a one-line suggestion while conducting a pull request review. Click on the button circled in red, and you’ll get a suggestion fenced code block, as shown.


Inside the code block is the current code; replace it with your proposed change. GitHub will present your suggestion in a nice diff format.

When the pull requester views your suggestion, they can accept the change with one click. Efficient!

Multiple-lines are not yet supported, according to this GitHub blog post it is a frequently requested feature.

h/t Jed and Raelyn

Pivotal Tracker Advanced Search by Owner

On a big project, Pivotal Tracker contains a wealth of data. Let’s use it!

Today I filtered all the stories assigned to my teammate with the owner keyword:

owner, owned_by
owner: “Obi Wan Kenobi”
Search using the full name, initials, the username, user id or part of the user’s name.

This filtered out hundreds of stories, leaving the four in progress that I care about.

Here’s the official advanced search guide. Try a new keyword today!

Change PostgreSQL psql prompt colors

Today I learned how to change psql prompt to add some color and manipulate which info to show:

$ psql postgres
postgres=# \set PROMPT1 '%[%033[1;32m%]@%/ => %[%033[0m%]%'
@postgres => \l
                                           List of databases
            Name            |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges
 postgres                   | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0                  | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
                            |            |          |             |             | postgres=CTc/postgres
 template1                  | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
                            |            |          |             |             | postgres=CTc/postgres
(3 rows)


Check this documentation if you want to know more.

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[]

I must use type assertion when passing tuples:

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