Last active
March 20, 2024 21:16
-
-
Save veevidify/e13786587ef1543ee7a60b03c550fea0 to your computer and use it in GitHub Desktop.
JS/TS snippets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
console.clear(); | |
const liftPromiseDelay = <T = unknown>(val: T): Promise<T> => new Promise(res => setTimeout(() => res(val), 2000)); | |
// library | |
type Result = {}; | |
const complexFetchParseRoutine = async (resourceUri: string): Promise<Result> => { | |
console.log('==library: ', 'fetch something'); | |
await liftPromiseDelay(resourceUri); | |
console.log('==library:', 'parse it'); | |
const res = await liftPromiseDelay(resourceUri[0]); | |
return res; | |
} | |
const complexCleanupRoutine = async (resourceUri: string): Promise<Result> => { | |
console.log('==library: ', 'clean up resource'); | |
return await liftPromiseDelay(resourceUri); | |
} | |
async function *generateResultsFromUris(uris: string[]): AsyncGenerator<Result> { | |
for (const uri of uris) { | |
yield await complexFetchParseRoutine(uri); | |
await complexCleanupRoutine(uri); | |
} | |
} | |
// consumer | |
const main = async () => { | |
const uris = ['a res a', 'b res b', 'c res c']; | |
for await (const result of generateResultsFromUris(uris)) { | |
console.log('=='); | |
console.log('==consumer: ', { result }); | |
console.log('=='); | |
} | |
} | |
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ... | |
performActions = () => { | |
const partitions = []; | |
const chunk = 10; | |
let partitionIndex = 0; | |
myArray.forEach(( t, i ) => { | |
if ( ! partitions[partitionIndex] ) { | |
partitions[partitionIndex] = []; | |
} | |
partitions[partitionIndex].push( t ); | |
if (( i + 1 ) % chunk === 0 ) { | |
partitionIndex = partitionIndex + 1; | |
} | |
}); | |
let batch = 1; | |
partitions[0].map( p => p.loading !== true | |
? this.props.mappedDispatch( p ) | |
: null | |
); | |
if ( partitions.length > 1 ) { | |
const batchSubmit = setInterval( _ => { | |
partitions[batch].map( p => p.loading !== true | |
? this.props.mappedDispatch( p ) | |
: null | |
); | |
batch ++; | |
if ( batch >= partitions.length ) { | |
clearInterval( batchSubmit ); | |
} | |
}, 12000 ); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// define a context function | |
const context = () => { | |
let c = 0; | |
// a closure lives within this higherOrder function lexical scope / context | |
const closure = () => { | |
c ++; | |
return c; | |
} | |
return closure; | |
}; | |
// here we initialize the entire scope itself | |
let inc = context(); | |
let c = inc(); // results 1 | |
let c1 = inc(); // results 2 | |
let inc2 = context(); | |
let c3 = inc2(); // results 1 | |
let c4 = inc2(); // results 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// explode dot-separated string: value into nested object | |
// as used in lodash _.set | |
// dotString = 'aa.bb.cc.dd', value = 'test' | |
const dotObject = (dotString, value) => | |
dotString.split('.').reverse().reduce( | |
(acc, cur) => ({ [cur]: acc }), | |
value | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type MyT = { | |
u?: string; | |
} | |
const filterProps = <T extends Object>(a: T[], prop: keyof T) => { | |
return a | |
.filter((item): item is Required<T> => !!item[prop]) | |
.map(item => item[prop]); | |
} | |
const myArr: MyT[] = [ | |
{ | |
u: 'a' | |
}, | |
{ | |
u: undefined | |
} | |
]; | |
const filtd = filterProps(myArr, 'u'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const _pipe = x => (...fns) => fns.reduce((acc, f) => f(acc), x); | |
// usage | |
_pipe(x)(f1, f2, f3); // x |> f1 |> f2 |> f3 | |
const _compose = (...fns) => fns.reduceRight((agg, f) => (...args) => agg(f(...args))); | |
// usage | |
const complexFunc = _compose(f1, f2, f3); | |
complexFunc(x); // f1 . f2 . f3 $ x | |
Array.prototype.pick = function(props) { return this.map(entry => props.reduce((mappedEntry, prop) => ({...mappedEntry, [prop]: entry[prop]}), {}) ) } | |
const _pick = (arr, props) => arr.map(elem => props.reduce((pickedElem, prop) => ({...pickedElem, [prop]: elem[prop]}), {})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const toUnixTime = (timeString: string) => { | |
let unixTime = Date.parse(timeString); | |
if (unixTime === NaN) unixTime = Date.parse('1970-01-01'); | |
return unixTime; | |
}; | |
const firstNInts = (n: number) => [...Array(n)].map((_, i) => i); | |
export const id = <T = any>(x: T) => x; | |
export const nop = (..._args: any) => {}; | |
export const noTask = new Promise<void>(nop); | |
export const liftPromise = <T>(resolution: T) => new Promise<T>(r => r(resolution)); | |
export const filterUndef = <T>(x: T | undefined): x is T => x !== undefined; | |
export const copyArrayObjects = <T>(a: T[]) => a.map(x => ({ ...x })); | |
export const getFromId = <T extends { id?: string }>(collection: T[], id: string) => | |
collection.find(b => b.id === id); | |
export function pipe(init: any) { | |
return (...fns: Function[]) => fns.reduce((agg: any, f: Function) => f(agg), init); | |
} | |
export function compose<A, B, C>(fn1: (b: B) => C, fn2: (a: A) => B): (a: A) => C; | |
// eslint-disable-next-line | |
export function compose<A, B, C, D>( | |
fn1: (c: C) => D, | |
fn2: (b: B) => C, | |
fn3: (a: A) => B, | |
): (a: A) => D; | |
export function compose(...fns: Function[]) { | |
return (args: any[]) => fns.reduceRight((agg, f) => f(agg), args); | |
} | |
export const flatMap = <T = any, U = any>(arr: T[], fn: (arg: T) => U[]): U[] => | |
arr.reduce((agg, c) => agg.concat([...fn(c)]), [] as U[]); | |
export const concat = <T = any>(...args: T[][]) => args.reduce((acc, t) => acc.concat(t), []); | |
export const createLookup = <T extends { id?: string }>(sources: T[]): { [key: string]: T } => | |
sources.reduce( | |
(idLookup, source) => | |
source.id | |
? { | |
...idLookup, | |
[source.id]: source, | |
} | |
: idLookup, | |
{}, | |
); | |
export const explode = (dotString: string, value: any) => | |
dotString | |
.split('.') | |
.reverse() | |
.reduce((acc, cur) => ({ [cur]: acc }), value); | |
export const omit = <T extends GObject>(obj: T) => (...args: (keyof T)[]): Partial<T> => | |
Object.keys(obj).reduce( | |
(strippedObj, key) => (!args.includes(key) ? { ...strippedObj, [key]: obj[key] } : strippedObj), | |
{} as Partial<T>, | |
); | |
export const revStr = (str: string) => str.split('').reduceRight((s, c) => s + c, ''); | |
export const mapTrue = (_: any) => true; | |
export const mapFalse = (_: any) => false; | |
const pick = <T, K extends keyof T>(obj: T, ...keys: (K)[]): Pick<T, K> => { | |
return keys.reduce((pickedObj, k) => keys.includes(k) | |
? { | |
...pickedObj, | |
[k]: pickedObj[k], | |
} | |
: pickedObj, {} as Pick<T, K>); | |
} | |
// lodash stuffs | |
declare const isArray: (arr) => arr is unknown[]; | |
declare const isObject: (obj) => obj is object; | |
type P = number | string | undefined | null | symbol | boolean | bigint; | |
type GObject = Record<string, unknown>; | |
const aToO = <T>(arr: T[], kPrefix = ""): Record<string, T> => { | |
if (!isArray(arr)) { | |
arr = [arr]; | |
} | |
return arr.reduce( | |
(record, val, ind) => ({ | |
...record, | |
[`${kPrefix === "" ? ind : [kPrefix, ind].join(".")}`]: val, | |
}), | |
{} | |
); | |
} | |
const walkSort = (obj: unknown | unknown[]): typeof obj => { | |
if (isArray(obj)) { | |
const children = obj.map(v => walkSort(v)); | |
const sortedInds = children | |
.map((elem, ind) => ({ | |
stringified: JSON.stringify(elem), | |
ind | |
})) | |
.sort((lhs, rhs) => lhs.stringified.localeCompare(rhs.stringified)) | |
.map(({ ind }) => ind); | |
return sortedInds.map(ind => children[ind]); | |
} else if (isObject(obj)) { | |
const children = mapValues(obj, v => walkSort(v)); | |
const sortedKeyEntries = Object.entries(children).sort(([lhsKey, _l], [rhsKey, _r]) => lhsKey.localeCompare(rhsKey)); | |
return Object.fromEntries(sortedKeyEntries); | |
} else { | |
return obj; | |
} | |
} | |
const flat = (o: object | P) => { | |
if (isArray(o)) { | |
o = aToO(o); | |
} | |
if (isObject(o)) { | |
return Object.keys(o).reduce((flattened, objKey) => { | |
const child = (o as GObject)[objKey]; | |
if (!isObject(child)) { | |
return { | |
...flattened, | |
[objKey]: child, | |
}; | |
} | |
let flatChild = flat(child) as GObject | P; | |
if (isArray(flatChild)) { | |
flatChild = aToO(flatChild, objKey); | |
} | |
if (isObject(flatChild)) { | |
return { | |
...flattened, | |
...Object.keys(flatChild).reduce((flattenedChild, childKey) => ({ | |
...flattenedChild, | |
[`${objKey}.${childKey}`]: (flatChild as GObject)[childKey], | |
}), {} as GObject) | |
}; | |
} else { | |
return { | |
...flattened, | |
[objKey]: flatChild | |
} | |
} | |
}, {} as GObject); | |
} else { | |
return o; | |
} | |
} | |
const maskString = (a: string) => Array(a.length).fill('#').join(''); | |
const maskInt = (i: number) => parseInt(Array(Math.floor(i).toString().length).fill('9').join(''), 10); | |
const maskNum = (a: number) => { | |
const intPart = Math.floor(a); | |
const decPart = parseInt(a.toString().split('.')[1] ?? '0', 10); | |
const maskedInt = maskInt(intPart); | |
const maskedDec = parseFloat(`0.${maskInt(decPart)}`); | |
return maskedInt + maskedDec; | |
} | |
type GO = { [k: string]: any }; | |
type Prim = string | null | undefined | number | boolean; | |
const checkSensitive = (path: string[], sensitiveKeys: string[]) => path.some(p => sensitiveKeys.includes(p)); | |
const recursiveMask = (o: GO | Prim | Array<any>, sensitiveKeys: string[], path: string[] = []): typeof o => { | |
const isSensitive = checkSensitive(path, sensitiveKeys); | |
if (typeof o === 'string') { | |
if (isSensitive) { | |
return maskString(o); | |
} else return o; | |
} | |
if (typeof o === 'number') { | |
if (isSensitive) { | |
return maskNum(o); | |
} else return o; | |
} | |
if (typeof o === 'object') { | |
if (o === null) return o; | |
if (Array.isArray(o)) { | |
return o.map(v => recursiveMask(v, sensitiveKeys, path)); | |
} else { | |
const maskedEntries = Object.entries(o).map(([key, v]) => [key, recursiveMask(v, sensitiveKeys, [...path, key])]); | |
return Object.fromEntries(maskedEntries); | |
} | |
} | |
return o; | |
} | |
const deepCp = <T>(o: T): T => { | |
if (typeof o === 'object') { | |
if (o === null) return o; | |
if (Array.isArray(o)) { | |
return o.map(item => deepCp(item)) as T; | |
} | |
return Object.fromEntries(Object.entries(o).map(([k, v]) => [k, deepCp(v)])) as T; | |
} | |
return o; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
console.clear(); | |
const errors = { | |
TYPE_A: { | |
code: 100, | |
message: "Error message for A", | |
}, | |
TYPE_B: { | |
code: 101, | |
message: "Error message for B", | |
} | |
} as const; | |
type ErrorType = keyof typeof errors; | |
type ErrorProperties = typeof errors[ErrorType]; | |
class MyError extends Error { | |
private _code: number; | |
constructor(errorType: ErrorType) { | |
const errorDetails = errors[errorType]; | |
super(errorDetails.message); | |
this._code = errorDetails.code; | |
} | |
get code(): number { | |
return this._code; | |
} | |
} | |
const invariant = (assertion: boolean, errorType: ErrorType): true | never => { | |
if (!assertion) { | |
throw new MyError(errorType); | |
} | |
return true; | |
} | |
try { | |
const fallThroughA = true; | |
const blockedA = false; | |
console.log("check0"); | |
invariant(fallThroughA, "TYPE_A"); | |
console.log("check1"); | |
invariant(blockedA, "TYPE_A"); | |
console.log("check2"); | |
} catch {} | |
try { | |
const fallThroughB = true; | |
const blockedB = false; | |
console.log("check0"); | |
invariant(fallThroughB, "TYPE_B"); | |
console.log("check1"); | |
invariant(blockedB, "TYPE_B"); | |
console.log("check2"); | |
} catch {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Base extends Object { | |
public baseProp: string | |
constructor() { | |
super(); | |
this.baseProp = 'b'; | |
} | |
} | |
class Derived extends Base { | |
public derivedProp: string | |
constructor() { | |
super(); | |
this.derivedProp = 'd'; | |
} | |
} | |
const d = new Derived(); | |
const b: Base = d; // covariant assignment Base <- Derived | |
// compiled | |
const eb = new Base(); | |
const ed: Derived = eb; // compile error: Property 'derivedProp' is missing | |
// in type 'Base' but required in type 'Derived'. | |
type GetStringProp<T> = (instance: T) => string; | |
const gb: GetStringProp<Base> = baseInstance => baseInstance.baseProp; | |
const gd: GetStringProp<Derived> = gb; // contravariant assignment GetStringProp<Derived> <- GetStringProp<Base> | |
// compiled | |
const resb = gd({ baseProp: 'b', derivedProp: 'd' }); // const gd: (instance: Derived) => string | |
// const reb: string | |
console.log(`resb=${resb}`); // resb=b | |
const egd: GetStringProp<Derived> = arg => arg.derivedProp; | |
const egb: GetStringProp<Base> = egd; // ?? | |
const resd = egb({ baseProp: 'b' }); // still compiled, type-checked with | |
// const egb: (instance: Base) => string | |
// const resd: string | |
console.log(`resd=${resd}`); // resd=undefined | |
console.log(resd.length); // Uncaught TypeError: Cannot read property 'length' of undefined |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// inferred: Promise<number> | |
const pass = async () => { | |
console.log("pass action"); | |
return Promise.resolve(1); | |
} | |
// inferred: Promise<number> but always reject | |
const fail = async () => { | |
if (Math.random()) { | |
console.log("!!fail action!!"); | |
throw new Error('rejected promise'); | |
} | |
return 1; | |
} | |
// actual inferred: Promise<Promise<number>[]> | |
const bundlingActions = async () => { | |
// need to fetch something to create this bundled actions | |
await pass(); | |
// mistake 1: seemingly return a list of promises since function async signifies promise | |
// actually returning promise of list of promises | |
return [ | |
pass(), | |
pass(), | |
fail(), | |
pass() | |
]; | |
} | |
const handler = async () => { | |
const multiActions = [ | |
pass(), | |
pass(), | |
bundlingActions() /// mistake 2: not spreading this since the intention is to await Promise.all | |
]; | |
try { | |
await Promise.all(multiActions); | |
console.log("passed"); | |
} catch (err) { | |
console.log("we caught the err! pass it to error handler"); | |
// next(err); | |
} | |
} | |
handler(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface HKT { | |
input: unknown, | |
output: unknown, | |
}; | |
type CallHKT<F extends HKT, I> = | |
(F & { input: I })["output"]; | |
interface ArrayHKT extends HKT { | |
output: Array<this["input"]> | |
}; | |
// Array<number> | |
type X = CallHKT<ArrayHKT, number>; | |
// === // | |
type Narrow<T> = | |
| (T extends infer U ? U : never) | |
| Extract<T, number | string | boolean | bigint | symbol | null | undefined | []> | |
| ([T] extends [[]] ? [] : { [K in keyof T]: Narrow<T[K]> }) | |
const narrow = <X>(x: Narrow<X>): X => { | |
return x as X; | |
} | |
// === // | |
type A = { kind: "A", a: string }; | |
type B = { kind: "B", b: number }; | |
type C = A | B; | |
declare const func: <K extends C["kind"]>(k: K) => Extract<C, { kind: K }> // switch (k) etc etc | |
type NarrowA = Extract<A, C> | |
type NarrowB = Extract<B, C> | |
const a = func("A"); | |
const b = func("B"); | |
const c = func("C"); | |
// === // | |
interface GenericFn { | |
(...args: any): void | |
} | |
function declareInferrableType<T extends Record<string, GenericFn>>(f: T) { | |
return f; | |
} | |
const indexed = declareInferrableType({ | |
'a': () => { }, | |
'b': (n: string) => { }, | |
c: 5, | |
}) | |
type A = typeof indexed['a'] | |
type B = typeof indexed['b'] | |
// === // | |
type DeepReadonly<T> = | |
T extends (infer R)[] ? DeepReadonlyArray<R> : | |
T extends Function ? T : | |
T extends object ? DeepReadonlyObject<T> : | |
T; | |
type RequireOnly<T, K extends keyof T> = Required<Pick<T, K>> & T; | |
// === // | |
// expands object types one level deep | |
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never; | |
// expands object types recursively | |
type ExpandRecursively<T> = T extends object | |
? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never | |
: T; | |
// === // | |
const keys = ["a", "b", "c"] as const | |
type Key = typeof keys[number] | |
type Shape = {x: string} | |
type Expected = {a: Shape} | {b: Shape} | {c: Shape} | |
type MyUnion = {[K in Key]: {[P in K]: Shape}}[Key] | |
// ^? | |
// === // | |
type InpA = { kind: 'a', a: string }; | |
type InpB = { kind: 'b', b: number }; | |
type Inp = InpA | InpB; | |
type RetA = { retA: number }; | |
type RetB = { retB: string }; | |
type Ret<AB extends Inp> = | |
AB extends InpA ? RetA : | |
AB extends InpB ? RetB : | |
never; | |
const condRet = <I extends Inp>(cond: I) => { | |
switch (cond.kind) { | |
case 'a': | |
return { retA: 1 } as Ret<I>; | |
case 'b': | |
return { retB: '2' } as Ret<I>; | |
} | |
} | |
const r1 = condRet({kind: 'a', a: 'a'}); | |
// ^? | |
const r2 = condRet({kind: 'b', b: 2}); | |
// ^? | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const withTryCatch = <TParams extends unknown[], TResult>(coroutine: (...args: TParams) => Promise<TResult>) => { | |
const wrappedRoutine = async (...args: TParams): Promise<TResult> => { | |
try { | |
const unwrapCoroutineResult = await coroutine(...args); | |
return unwrapCoroutineResult; | |
} catch (err) { | |
// process err | |
console.log({ err }); | |
throw err; | |
} | |
} | |
return wrappedRoutine; | |
} | |
type ApiResponse = {}; type IOResponse = {}; | |
const myCoroutine1 = async (someArg: number, someOtherArg: string): Promise<ApiResponse> => Promise.resolve({}); | |
const myCoroutine2 = async (someArg: boolean, someOtherArg: number): Promise<IOResponse> => Promise.reject(Error('some err')); | |
class Empty {} | |
const wrapped1 = withTryCatch(myCoroutine1); | |
const wrapped2 = withTryCatch(myCoroutine2); | |
const wrappedInferred = withTryCatch( | |
async (arg1: object, arg2: string[]) => Promise.resolve(new Empty()) | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment