Last active
September 26, 2018 03:38
-
-
Save rjdestigter/bf1f6b9330371c66421abeaac14d80a6 to your computer and use it in GitHub Desktop.
Composable getters and setters with TypeScript
This file contains hidden or 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 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.