Skip to content

Instantly share code, notes, and snippets.

@kdgerona
Created April 25, 2023 02:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kdgerona/cb47bf1ba28d7dffd182a158267844cd to your computer and use it in GitHub Desktop.
Save kdgerona/cb47bf1ba28d7dffd182a158267844cd to your computer and use it in GitHub Desktop.
Typescript decorators sample application
// import 'reflect-metadata'
type TCallback = (prev?: any) => void
type TSetStateParam = TCallback | any
type SetState = (param: TSetStateParam) => void
// Property Decorator
const UseState = function(initialValue: any) {
return function(target: any, key: string) {
const executeEffects = function() {
const metadata = Reflect.getMetadata(key, target)
const { callbacks = [] } = metadata ?? {}
callbacks.forEach((callback: string) => {
const method = Object.getOwnPropertyDescriptor(target, callback)
if(!method) return
const fnValue = method.value
// @ts-ignore
fnValue.apply(this)
// // @ts-ignore
// method?.value.apply(this)
})
}
target[key] = initialValue
target[`set${key.replace(/^\w/, c => c.toUpperCase())}`] = function(param: TSetStateParam){
const prevValue = target[key]
if(typeof param === 'function'){
const result = param(prevValue)
target[key] = result
executeEffects.bind(this)()
return
}
target[key] = param
executeEffects.bind(this)()
}
}
}
// Method Decorator
const UseEffect = (dependencies: Array<string>): MethodDecorator => (target: Object, key: string | symbol, descriptor: PropertyDescriptor) => {
Reflect.defineMetadata(key, {dependencies}, target)
return descriptor
}
// Class Decorator
const WithHooks = (): ClassDecorator => (target) => {
const methods = Object.getOwnPropertyNames(target.prototype)
methods.forEach(methodName => {
const metadata = Reflect.getMetadata(methodName, target.prototype)
if(!metadata) return
const { dependencies = [] } = metadata
dependencies.forEach((dependency: string) => {
const variable = Object.getOwnPropertyDescriptor(target.prototype, dependency)
if(!variable) return
const metadata = Reflect.getMetadata(dependency, target.prototype)
const { callbacks = [] } = metadata ?? {}
Reflect.defineMetadata(dependency, {
...(metadata ?? {}),
callbacks: [...callbacks, methodName]
}, target.prototype)
})
})
}
@WithHooks()
class Component {
@UseState(10)
public count?: number; setCount?: SetState
@UseEffect(['count'])
counterLogger() {
console.log('CURRENT COUNT: ', this.count)
}
}
// Render Component
const renderComponent = new Component()
// console.log('count', renderComponent.count)
renderComponent.setCount?.(2)
// console.log('count2', renderComponent.count)
renderComponent.setCount?.((prev: any) => {
return prev + 2
})
// console.log('count4', renderComponent.count)
// Method
// console.log('Method Metadata', Reflect.getMetadata('counterLogger', renderComponent))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment