type ValueTracker = { getValue ( ) : string ; setValue ( value ) : void ; stopTracking ( ) : void ; } ; type WrapperState = { _valueTracker ? : ValueTracker ; } ; type ElementWithValueTracker = HTMLInputElement & WrapperState ; const getTracker = ( node ) : ValueTracker | undefined => { return node . _valueTracker ; } ; const isCheckable = ( elem ) => { const type = elem . type ; const nodeName = elem . nodeName ; return ( nodeName && nodeName . toLowerCase ( ) === 'input' && ( type === 'checkbox' || type === 'radio' ) ) ; } ; const detachTracker = ( node ) : void => { node . _valueTracker = undefined ; } ; const trackValueOnNode = ( node ) : ValueTracker | undefined => { const valueField = isCheckable ( node ) ? 'checked' : 'value' ; const descriptor = Object . getOwnPropertyDescriptor ( node . constructor . prototype , valueField ) ; let currentValue = '' + node [ valueField ] ; if ( ! descriptor ) return ; const { get , set } = descriptor ; Object . defineProperty ( node , valueField , { configurable : true , get ( ) { return get ?. call ( this ) ; } , set ( value ) { currentValue = '' + value ; set ?. call ( this , value ) ; } , } ) ; const tracker = { getValue ( ) { return currentValue ; } , setValue ( value ) { currentValue = '' + value ; } , stopTracking ( ) { detachTracker ( node ) ; delete node [ valueField ] ; } , } ; return tracker ; } ; export const track = ( node ) => { if ( getTracker ( node ) ) return ; node . _valueTracker = trackValueOnNode ( node ) ; } ; const getValueFromNode = ( node ) : string => { let value = '' ; if ( ! node ) { return value ; } if ( isCheckable ( node ) ) value = node . checked ? 'true' : 'false' ; else { value = node . value ; } return value ; } ; export const updateValueIfChanged = ( node ) : boolean => { if ( ! node ) return false ; const tracker = getTracker ( node ) ; if ( ! tracker ) return true ; const lastValue = tracker . getValue ( ) ; const nextValue = getValueFromNode ( node ) ; if ( nextValue !== lastValue ) { tracker . setValue ( nextValue ) ; return true ; } return false ; } ;