Skip to content

Instantly share code, notes, and snippets.

@nielk
Created April 11, 2023 04:31
Show Gist options
  • Save nielk/b177888071a2eb1e89c1a5074253d13c to your computer and use it in GitHub Desktop.
Save nielk/b177888071a2eb1e89c1a5074253d13c to your computer and use it in GitHub Desktop.
Typesafe getContext setContext (typescript)
<script lang="ts">
import { Context } from './contexts'
import { getContext } from './getContext'
const loggedUser = getContext(Context.User)
</script>
import { Readable, Writable } from 'svelte/store'
import { isReadableOf, isWritableOf } from './svelte'
import { User } from './User'
export const UserStore: InjectionKey<Writable<User>> = isWritableOf(
(v: any): v is UserStoreType => {
return (
v !== null && 'lineNames' in v && 'shortName' in v && 'fullName' in v && 'role' in v
)
}
)
export const Context = {
User: UserStore,
}
import { getContext as svelteGetContext, setContext as svelteSetContext } from 'svelte'
export type InjectionKey<T = unknown> = (v: any) => v is T
type getContext = {
<T>(key: InjectionKey<T>): T
}
export const getContext: getContext = <T>(key: InjectionKey<T>) => {
const value = svelteGetContext(key)
if (key(value)) {
return value
} else {
throw new Error(`Context ${key} is invalid (missing or wrong type)`)
}
}
<script lang="ts">
import { Context } from './contexts'
import { setContext } from './setContext'
setContext(Context.User, {
name: 'foo',
shortName: 'bar',
fullName: 'Foo Bar',
role: 'user',
lineNames: ['1', '2', '3']
})
</script>
import { InjectionKey } from './getContext'
type setContext = <T>(key: InjectionKey<T>, context: T) => void
export const setContext = svelteSetContext as setContext
import { get, Readable, Writable } from 'svelte/store'
// Type guards
export const isWritable = <T>(value: any): value is Writable<T> => {
return value !== null && 'set' in value && 'subscribe' in value && 'update' in value
}
export const isWritableOf = <T>(typeguard: (v: any) => v is T) => {
return (value: any): value is Writable<T> => isWritable(value) && typeguard(get(value))
}
export const isReadable = <T>(value: any): value is Readable<T> => {
return (
value !== null && 'subscribe' in value && !('set' in value) && !('update' in value)
)
}
export const isReadableOf = <T>(typeguard: (v: any) => v is T) => {
return (value: any): value is Readable<T> => isReadable(value) && typeguard(get(value))
}
export type User = {
name: string,
shortName: string,
fullName: string,
role: string,
lineNames: ReadonlyArray<string>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment