Skip to content

Instantly share code, notes, and snippets.

View alii's full-sized avatar
🗿
compuoter

Alistair Smith alii

🗿
compuoter
View GitHub Profile
@alii
alii / wrap-redis.ts
Last active September 26, 2023 23:07
wrap-redis.ts
async function wrapRedis<T>(key: string, fn: () => Promise<T>, seconds = 60): Promise<T> {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const recent = await fn();
if (recent) {
await redis.set(key, JSON.stringify(recent), 'ex', seconds);
}
@alii
alii / use-throttle.ts
Created July 7, 2021 00:14
use-throttle.ts
export function useThrottle<T>(value: T, limit = 1000) {
const [throttledValue, setThrottledValue] = useState(value);
const lastRan = useRef(Date.now());
useEffect(() => {
const handler = setTimeout(() => {
if (Date.now() - lastRan.current >= limit) {
setThrottledValue(value);
lastRan.current = Date.now();
}
@alii
alii / ensure-keys.ts
Created August 25, 2021 14:12
Infer object values while strictly specifying keys in TypeScript
/**
* TypeScript currently lacks a way of typing a record of keys only but not values.
* we need this because we want an object of typed keys but we want to leave the values inferred.
* thankfully, we can do this with generics. This function allows the second generic's values to be inferred
* so that our object is fully type safe for hugely complex types, but we can guarantee that all the keys we need exist.
* It's not perfect, but it gets the job done for the time being
*/
export function ensureKeys<T>() {
/**
* Function that returns the value that gets put in, values are type-safely inferred
/**
* Handle clicks outside of an element.
* This is useful for closing a modal by clicking outside of the modal.
* @param callback - The callback function to run when clicking outside of an element
*/
export function useOutsideClick<E extends HTMLElement = HTMLElement>(callback: () => unknown) {
const container = useRef<E | null>(null);
useEffect(() => {
const handler = (event: MouseEvent) => {
@alii
alii / validator.ts
Last active April 13, 2022 20:58
Small schema validator inspired by zod. Built so I could learn to some degree how zod works under the hood.
interface Schema<Out> {
parse(value: unknown, path: string): Out;
}
type Resolve<T> = T extends Schema<infer R> ? Resolve<R> : T;
function bool(): Schema<boolean> {
return {
parse(value, path) {
const valid = typeof value === "boolean";
@alii
alii / create-modal.tsx
Created March 10, 2022 15:00
HeadlessUI Modal Factory
import {Dialog, Transition} from '@headlessui/react';
import React, {Fragment, ReactNode} from 'react';
import {BiX} from 'react-icons/bi';
interface Props<Y> {
isOpen: boolean;
close: () => unknown;
// Required because used in the callback
// eslint-disable-next-line react/no-unused-prop-types
@alii
alii / scalable-fizzbuzz.ts
Created February 2, 2022 12:09
Overengineering a basic interview question by making the a scalable version of FizzBuzz.
interface Interceptor {
condition(i: number): boolean;
execute(i: number): void;
}
abstract class FizzBuzzUtils {
protected isMultipleOf(a: number, multiplier: number) {
return a % multiplier === 0;
}
}
@alii
alii / is-instance.ts
Created January 19, 2022 14:00
Useful error checking for promise chaining
type Constructor<T> = new (...args: any[]) => T;
type Then<T, R> = (value: T) => R;
export function ifInstance<T, R>(instance: Constructor<T>, then: Then<T, R>) {
return (value: unknown) => {
if (value instanceof instance) {
return then(value);
}
throw value;
@alii
alii / multiply.ts
Created December 31, 2021 12:00
Multiplication math in TypeScript's type system
// Multiplication in raw TypeScript. Please, please do not ever ever ever use this
type TupleOfLength<
T,
L extends number,
R extends T[] = []
> = R["length"] extends L ? R : TupleOfLength<T, L, [...R, T]>;
type FlattenResult<
T extends 0[][],
@alii
alii / handler-for.ts
Last active December 8, 2021 21:47
An easy way to not have to remember React event handlers types in TypeScript
type RemoveOn<T extends string> = T extends `on${infer R}` ? R : T;
type PropsFor<El extends keyof JSX.IntrinsicElements> = JSX.IntrinsicElements[El];
type OnProps<El extends keyof JSX.IntrinsicElements> = Uncapitalize<RemoveOn<Extract<keyof PropsFor<El>, `on${string}`>>>;
type HandlerFor<El extends keyof JSX.IntrinsicElements, Ev extends OnProps<El>> = NonNullable<PropsFor<El>[`on${Capitalize<Ev>}` & keyof PropsFor<El>]>;
// Creating with a callback
function handlerFor<El extends keyof JSX.IntrinsicElements, Ev extends OnProps<El>>(event: [El, Ev], handler: HandlerFor<El, Ev>) {
return handler;
}