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 |
/** @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 => { |
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"; |
https://www.typescriptlang.org/docs/handbook/interfaces.html#excess-property-checks
type MyObject = {
foo: string;
bar: string;
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"; |
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].
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 |
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[] => |