Skip to content

Instantly share code, notes, and snippets.

View OliverJAsh's full-sized avatar

Oliver Joseph Ash OliverJAsh

View GitHub Profile
@OliverJAsh
OliverJAsh / foo.md
Last active September 4, 2023 15:31
`Option` vs non-`Option`

Option vs non-Option

Option<T> non-Option (T | undefined)
accessing property userOption.map(user => user.age) userNullish?.age
calling a method userOption.map(user => user.fn()) userNullish?.fn()
providing fallback ageOption.getOrElse(0) ageNullish ?? 0
filter ageOption.filter(checkIsOddNumber) `ageNull
@OliverJAsh
OliverJAsh / foo.js
Created January 22, 2020 12:38
ESLint `no-logical-not`
/** @type {import('eslint').Rule.RuleModule} */
const noLogicalNot = {
meta: { fixable: 'code' },
create: context => {
/** @type {(node: import('estree').Node) => void} */
const listener = node => {
context.report({
node,
message: 'Prefer `x === false`.',
fix: fixer => {
@OliverJAsh
OliverJAsh / foo.js
Last active January 5, 2020 14:28
Single pass array transformations with IxJS (iterables + generators)
const add1 = n => n + 1;
const square = n => n * n;
//
// Array methods
//
// Each time a method is called, a new array is immediately constructed.
[1, 2, 3, 4]
// ./api.js
export const getPhoto = () => {
/* … */
};
export const getUser = () => {
/* … */
};
export * as Api from "./api";
@OliverJAsh
OliverJAsh / foo.md
Last active September 19, 2019 13:52
TypeScript excess property checks: caveats and workarounds
@OliverJAsh
OliverJAsh / foo.md
Last active August 23, 2019 10:06
Naming pipeable operators

Naming pipeable operators

Many libraries are moving from instance methods to [pipeable operators] (e.g. RxJS, fp-ts, idb-keyval, Fluture, date-fns) because they have better bundling performance.

This works really well when you're only importing pipeable operators for one type (e.g. Observable from RxJS), but if we're working with multiple types (e.g. Observable and Option from fp-ts) in the same file, you will inevitably face the problem of naming conflicts. For example:

import { map } from "rxjs/operators";
import { map } from "fp-ts/lib/Option";
// ./api.js
export const getPhoto = () => {
/* … */
};
export const getUser = () => {
/* … */
};
import * as Api from "./api";

Named namespace imports

If you're organising JavaScript/TypeScript code into [modules], at some point you're going to need to consider how you're naming your imports and exports. At Unsplash, the issue we've found is that names either have too little information or too much—the former leads to naming conflicts and the latter leads to very long names.

[Namespace imports][namespace imports] are designed to help with this, but they have several disadvantages when compared with [named imports]. This article introduces named namespace imports, a technique which we've adopted at Unsplash to combine (as the name suggests) the best of both [namespace imports] and [named imports].

Namespace imports

Imagine we have an API module which exports some functions corresponding to API endpoints:

Dev UX: auto imports Dev UX: contextual naming Perf: tree shaking Dev UX: namespace/type merging
namespace import not yet yes maybe no
TS namespace yes yes no yes
types: namespace import / values: named import not yet / yes yes / no yes no
types: TS namespace / values: named import yes yes / no yes yes
@OliverJAsh
OliverJAsh / foo.ts
Created August 9, 2019 18:16
A negate function for type guards
type TypeGuard<T, U extends T> = (value: T) => value is U;
const negate = <T, U extends T>(
typeGuard: TypeGuard<T, U>,
): TypeGuard<T, Exclude<T, U>> => (value): value is Exclude<T, U> =>
typeGuard(value) === false;
// Example
const checkIsArray = <T>(value: T | any[]): value is any[] =>