Skip to content

Instantly share code, notes, and snippets.

@gcanti
Created September 21, 2017 15:23
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gcanti/da641d57dc6ebbdd0b1f4b2eb7e3394e to your computer and use it in GitHub Desktop.
Save gcanti/da641d57dc6ebbdd0b1f4b2eb7e3394e to your computer and use it in GitHub Desktop.
Type-safe get, set, remove, pick, insert with TypeScript
import { ObjectOmit } from 'typelevel-ts'
const get = <O, K extends keyof O>(k: K, o: O): O[K] => o[k]
const set = <O, K extends keyof O>(k: K, v: O[K], o: O): O => Object.assign({}, o, { [k as any]: v })
const remove = <O, K extends keyof O>(k: K, o: O): ObjectOmit<O, K> => {
const copy: any = Object.assign({}, o)
delete copy[k]
return copy
}
const pick = <O, K extends keyof O>(ks: [K], o: O): Pick<O, K> => {
const copy: any = {}
ks.forEach(k => {
copy[k] = o[k]
})
return copy
}
const insert = <O>() => <K extends keyof O>(k: K, v: O[K], o: ObjectOmit<O, K>): O =>
Object.assign({}, o, { [k as any]: v }) as any
//
// tests
//
const get1: number = get('a', { a: 1 }) // ok
console.log(get1) // => 1
// const get2: string = get('a', { a: 1 }) // error
// const get3: string = get('b', { a: 'sdf' }) // error
const set1 = set('a', 2, { a: 1 }) // ok
console.log(set1) // => { a: 2 }
// const set2 = set('a', 'foo', { a: 1 }) // error
// const set3 = set('b', 2, { a: 1 }) // error
// ugly displayed type though
/*
const remove1: Pick<{
a: number;
b: string;
c: boolean;
}, "a" | "c">
*/
const remove1 = remove('b', { a: 1, b: 'foo', c: true })
console.log(remove1) // => { a: 1, c: true }
// const remove2 = remove('b', { a: 1, c: true }) // error
const pick1 = pick(['a'], { a: 1, b: 'foo', c: true })
console.log(pick1) // => { a: 1 }
const pick2 = pick(['a', 'c'], { a: 1, b: 'foo', c: true })
console.log(pick2) // => { a: 1, c: true }
// const pick3 = pick(['d'], { a: 1, b: 'foo', c: true }) // error
type MyRecord = { a: number; b: string; c: boolean }
const myinsert = insert<MyRecord>()
const insert1 = myinsert('a', 1, { b: 'foo', c: true })
console.log(insert1) // => { b: 'foo', c: true, a: 1 }
// const insert2 = myinsert('d', 1, { b: 'foo', c: true }) // error
// const insert3 = myinsert('a', 2, { a: 1, b: 'foo', c: true }) // error
const x = { a: 1, b: 'foo', c: true }
const insert4 = myinsert('a', 2, x) // NO error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment