Skip to content

Instantly share code, notes, and snippets.

View OliverJAsh's full-sized avatar

Oliver Joseph Ash OliverJAsh

View GitHub Profile
// ./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[] =>
@OliverJAsh
OliverJAsh / test.ts
Created August 2, 2019 15:36
TypeScript narrow non-discriminated union
{
type AncestryType = {
type: string;
};
type AncestryCategory = {
type: string;
category: string;
};
@OliverJAsh
OliverJAsh / env.ts
Created August 2, 2019 14:03
TypeScript narrowed union is lost inside function scopes
export type Foo = {
foo: string;
};
export type Bar = {
bar: string;
};
export type Value = Foo | Bar;
export declare const value: Value;
@OliverJAsh
OliverJAsh / foo.sh
Created August 1, 2019 16:07
git-pamlog
#!/bin/bash
# https://junegunn.kr/2016/07/fzf-git/
# https://hackernoon.com/be-125-more-efficient-with-git-60556a1ce971
# https://github.com/fcsonline/dotfiles/blob/master/git/gitconfig
# https://github.com/junegunn/fzf/wiki/Examples#git
# TODO: incorporate fix into this?
# TODO: select commits for editing, generate rebase from that