Skip to content

Instantly share code, notes, and snippets.

@esamattis
Created April 9, 2018 07:45
Show Gist options
  • Save esamattis/b100b5e6387934d9dffa93da7eabd349 to your computer and use it in GitHub Desktop.
Save esamattis/b100b5e6387934d9dffa93da7eabd349 to your computer and use it in GitHub Desktop.
type DeepReadonly<T> =
T extends [infer A] ? DeepReadonlyObject<[A]> :
T extends [infer A, infer B] ? DeepReadonlyObject<[A, B]> :
T extends [infer A, infer B, infer C] ? DeepReadonlyObject<[A, B, C]> :
T extends [infer A, infer B, infer C, infer D] ? DeepReadonlyObject<[A, B, C, D]> :
T extends [infer A, infer B, infer C, infer D, infer E] ? DeepReadonlyObject<[A, B, C, D, E]> :
T extends [infer A, infer B, infer C, infer D, infer E, infer F] ? DeepReadonlyObject<[A, B, C, D, E, F]> :
T extends [infer A, infer B, infer C, infer D, infer E, infer F, infer G] ? DeepReadonlyObject<[A, B, C, D, E, F, G]> :
T extends (infer A)[] ? DeepReadonlyArray<A> :
T extends Function ? T : // can change to never to forbid functions
T extends Map<infer U, infer V> ? ReadonlyMap<DeepReadonlyObject<U>, DeepReadonlyObject<V>> :
T extends Set<infer U> ? ReadonlySet<DeepReadonlyObject<U>> :
T extends object ? DeepReadonlyObject<T> :
T
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
type DeepReadonlyObject<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> }
type DeepTestType = DeepReadonly<{
a: string
b: number[]
c: { d: string, e: number[], f: [number, { g: string, h: number }] }
i: [number, string, boolean]
j: { id: number, name: string }[]
k: Set<number>
l: Map<{ id: number, name: string }, { age: number }>
m(): void
n: Symbol
o: Promise<void>
}>
const record = { id: 123, name: 'zxc' }
const deepTest: DeepTestType = {
a: 'asdf',
b: [123, 234],
c: { d: 'zxc', e: [1, 2, 3], f: [1, { g: 'iop', h: 789 }] },
i: [1, 'a', true],
j: [{ id: 1, name: 'c' }],
k: new Set([1, 2, 3]),
l: new Map([
[record, { age: 789 }]
]),
m() { console.log('m says mmm') },
n: Symbol(),
o: Promise.resolve()
}
console.log(deepTest.a) // pass
deepTest.a = 'zxcv' // fail
console.log(deepTest.b[0]) // pass
deepTest.b[0] = 789 // fail
deepTest.b[1] = 890 // fail
console.log(deepTest.c.d) // pass
deepTest.c.d = 'asd' // fail
console.log(deepTest.c.f[0]) // pass
deepTest.c.f[0] = 'asd' // fail
console.log(deepTest.c.f[1].g) // pass
deepTest.c.f[1].g = 'zxc' // fail
console.log(deepTest.i[1]) // pass
console.log(Math.pow(deepTest.i[0], 1) // pass (doesn't union tuple)
deepTest.i[1] = 'zxc' // fail
console.log(deepTest.j[0].id) // pass
deepTest.j[0].id = 789 // fail
console.log(deepTest.k) // pass
deepTest.k.add(789) // fail
console.log(deepTest.l.get(record).age) // pass
deepTest.l.get(record).age = 1 // fail
deepTest.l.set({ id: 789, name: 'blah' }, { age: 567 }) // fail
for (let key of deepTest.l.keys()) {
console.log(key.name) // pass
key.name = 'zxc' // fail
}
deepTest.m() // pass
deepTest.m = () => console.log('n says nnn') // fail
console.log(deepTest.n) // pass
deepTest.n = Symbol() // fail
deepTest.o.then(() => console.log('hello')) // pass
deepTest.o = Promise.resolve()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment