import { createScalarNode , SimpleType , IType , TypeFlags , IValidationContext , IValidationResult , typeCheckSuccess , typeCheckFailure , AnyObjectNode } from "../../internal" ; export interface CustomTypeOptions < S , T > { /** Friendly name */ name : string ; /** given a serialized value and environment, how to turn it into the target type */ fromSnapshot ( snapshot , env ? ) : T ; /** return the serialization of the current value */ toSnapshot ( value ) : S ; /** if true, this is a converted value, if false, it's a snapshot */ isTargetType ( value ) : boolean ; /** a non empty string is assumed to be a validation error */ getValidationMessage ( snapshot ) : string ; } param options returns export function custom < S , T > ( options ) { return new CustomType < S , T > ( options ) ; } internal hidden export class CustomType < S , T > extends SimpleType < S | T , S , T > { readonly flags = TypeFlags . Custom ; constructor ( protected readonly options ) { super ( options . name ) ; } describe ( ) { return this . name ; } isValidSnapshot ( value , context ) { if ( this . options . isTargetType ( value ) ) return typeCheckSuccess ( ) ; const typeError = this . options . getValidationMessage ( value as S ) ; if ( typeError ) { return typeCheckFailure ( context , value , `Invalid value for type '${ this . name }': ${ typeError }` ) ; } return typeCheckSuccess ( ) ; } getSnapshot ( node ) { return this . options . toSnapshot ( node . storedValue ) ; } instantiate ( parent , subpath , environment , initialValue ) { const valueToStore = this . options . isTargetType ( initialValue ) ? ( initialValue as T ) : this . options . fromSnapshot ( initialValue as S , parent && parent . root . environment ) ; return createScalarNode ( this , parent , subpath , environment , valueToStore ) ; } reconcile ( current , value , parent , subpath ) { const isSnapshot = ! this . options . isTargetType ( value ) ; if ( ! current . isDetaching ) { const unchanged = current . type === this && ( isSnapshot ? value === current . snapshot : value === current . storedValue ) ; if ( unchanged ) { current . setParent ( parent , subpath ) ; return current ; } } const valueToStore = isSnapshot ? this . options . fromSnapshot ( value as S , parent . root . environment ) : ( value as T ) ; const newNode = this . instantiate ( parent , subpath , undefined , valueToStore ) ; current . die ( ) ; return newNode ; } }