Skip to content

Instantly share code, notes, and snippets.

@rjdestigter
Last active September 26, 2018 03:38
Show Gist options
  • Save rjdestigter/bf1f6b9330371c66421abeaac14d80a6 to your computer and use it in GitHub Desktop.
Save rjdestigter/bf1f6b9330371c66421abeaac14d80a6 to your computer and use it in GitHub Desktop.
Composable getters and setters with TypeScript
export function composeGetter<K extends string>(prop: K) {
function getter<T extends { [P in K]?: any }>(object: T): T[typeof prop]
function getter<T extends { [P in K]: any }>(object: T) {
return object[prop]
}
return getter
}
export function composeSetter<K extends string>(prop: K) {
function setter<T extends { [P in K]?: any }>(object: T, value: T[typeof prop]): T
function setter<T extends { [P in K]: any }>(object: T, value: T[typeof prop]): T {
return Object.assign(object, {
[prop]: value
})
}
return setter
}
export interface GetProperty<K extends string> {
<T extends { [P in K]?: any }>(object: T): T[K]
<T extends { [P in K]: any }>(object: T): T[K]
}
export interface SetProperty<K extends string> {
<T extends { [P in K]?: any }>(object: T, value: T[K]): T
<T extends { [P in K]: any }>(object: T, value: T[K]): T
}
// Examples
interface User {
id: number,
username: string,
}
type NewUser = Partial<User>
export const getUserId = composeGetter('id')
type UsernameGetter = GetProperty<'username'>
const getUsername: UsernameGetter = composeGetter('username')
declare const user: User
declare const newUser: NewUser
const userId = getUserId(user) // :number
const userUsername = getUsername(user) // :number | undefined
const newUserId = getUserId(newUser) // :number | undefined
const newUserUsername = getUsername(newUser) // :string | undefined
const nextUser = composeSetter('username')(newUser, 'foobar')
const nextUserUsername = nextUser.username // :string | undefined
@rjdestigter
Copy link
Author

TypeScript 3.0.3

@rjdestigter
Copy link
Author

This does not create strict functions that expect a specific interface. In the example getUserId could be applied to anything as long as it has .id defined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment