import { NeverTypeAssertion , AnyTypeAssertion , UnknownTypeAssertion , PrimitiveValueTypes , PrimitiveValueTypeNames , OptionalPrimitiveValueTypeNames , PlaceholderTypeNames , OptionalPlaceholderTypeNames , ErrorMessages , PrimitiveTypeAssertion , PrimitiveValueTypeAssertion , RepeatedAssertion , SpreadAssertion , SequenceAssertion , OneOfAssertion , OptionalAssertion , EnumAssertion , ObjectAssertionMember , AdditionalPropsKey , AdditionalPropsMember , ObjectAssertion , AssertionSymlink , AssertionOperator , TypeAssertion } from './types' ; import { dummyTargetObject , isUnsafeVarNames } from './lib/protection' ; export function picked ( ty , ... names ) { switch ( ty . kind ) { case 'object' : { const members = [ ] ; for ( const name of names ) { const member = ty . members . find ( x => x [ 0 ] === name ) ; if ( member ) { if ( member [ 2 ] ) { const m2 = [ ... member ] as any ; if ( 3 < m2 . length ) { m2 [ 2 ] = false ; } else { m2 . length = 2 ; } members . push ( m2 ) ; } else { members . push ( member ) ; } } } return ( { kind : 'object' , members , } ) ; } case 'symlink' : case 'operator' : { return ( { kind : 'operator' , operator : 'picked' , operands : [ ty , ... names ] , } ) ; } default : return ( { kind : 'object' , members : [ ] , } ) ; } } export function omit ( ty , ... names ) { switch ( ty . kind ) { case 'object' : { const members = [ ] ; for ( const member of ty . members ) { if ( ! names . find ( name => member [ 0 ] === name ) ) { if ( member [ 2 ] ) { const m2 = [ ... member ] as any ; if ( 3 < m2 . length ) { m2 [ 2 ] = false ; } else { m2 . length = 2 ; } members . push ( m2 ) ; } else { members . push ( member ) ; } } } return ( { kind : 'object' , members , } ) ; } case 'symlink' : case 'operator' : { return ( { kind : 'operator' , operator : 'omit' , operands : [ ty , ... names ] , } ) ; } default : return ( { kind : 'object' , members : [ ] , } ) ; } } export function partial ( ty ) { switch ( ty . kind ) { case 'object' : { const members = [ ] ; for ( const member of ty . members ) { let m = member [ 1 ] . kind === 'optional' ? member : [ member [ 0 ] , optional ( member [ 1 ] ) , ... member . slice ( 2 ) ] as ObjectAssertionMember ; if ( m [ 2 ] ) { m = [ ... m ] as any ; if ( 3 < m . length ) { m [ 2 ] = false ; } else { m . length = 2 ; } } m [ 1 ] . name = m [ 0 ] ; const optTy = { ... ( m [ 1 ] as OptionalAssertion ) . optional } ; ( m [ 1 ] as OptionalAssertion ) . optional = optTy ; if ( optTy . name && optTy . name !== optTy . typeName ) { delete optTy . name ; } if ( ! optTy . name && optTy . typeName ) { optTy . name = optTy . typeName ; } members . push ( m ) ; } return ( { kind : 'object' , members , } ) ; } case 'symlink' : case 'operator' : { return ( { kind : 'operator' , operator : 'partial' , operands : [ ty ] , } ) ; } default : return ty ; } } export function intersect ( ... types ) { if ( types . length === 0 ) { throw new Error ( `Empty intersection type is not allowed.` ) ; } if ( 0 < types . filter ( x => x && typeof x === 'object' && ( x . kind === 'symlink' || x . kind === 'operator' ) ) . length ) { return ( { kind : 'operator' , operator : 'intersect' , operands : types . slice ( ) , } ) ; } let lastTy = null ; const members = new Map < string , ObjectAssertionMember > ( ) ; for ( const ty of types ) { if ( ty && typeof ty === 'object' ) { if ( lastTy && lastTy . kind !== ty . kind ) { return ( { kind : 'never' , } ) ; } lastTy = ty ; if ( ty . kind === 'object' ) { for ( const m of ty . members ) { if ( m [ 2 ] ) { const m2 = [ ... m ] as any ; if ( 3 < m2 . length ) { m2 [ 2 ] = false ; } else { m2 . length = 2 ; } members . set ( m [ 0 ] , m2 ) ; } else { members . set ( m [ 0 ] , m ) ; } } } } else { return ( { kind : 'never' , } ) ; } } if ( lastTy && lastTy . kind !== 'object' ) { return lastTy ; } else { return ( { kind : 'object' , members : Array . from ( members . values ( ) ) , } ) ; } } export function oneOf ( ... types ) { if ( types . length === 0 ) { throw new Error ( `Empty union type is not allowed.` ) ; } if ( types . length === 1 ) { const ty = types [ 0 ] ; if ( ty && typeof ty === 'object' ) { return ty ; } else { return primitiveValue ( ty ) ; } } const ret = { kind : 'one-of' , oneOf : [ ] , } ; for ( const ty of types ) { if ( ty && typeof ty === 'object' ) { if ( ty . kind === 'one-of' ) { ret . oneOf = ret . oneOf . concat ( ty . oneOf ) ; } else { ret . oneOf . push ( ty ) ; } } else { ret . oneOf . push ( primitiveValue ( ty ) ) ; } } return ret ; } export function subtract ( ... types ) { if ( types . length === 0 ) { throw new Error ( `Empty subtraction type is not allowed.` ) ; } if ( 0 < types . filter ( x => x && typeof x === 'object' && ( x . kind === 'symlink' || x . kind === 'operator' ) ) . length ) { return ( { kind : 'operator' , operator : 'subtract' , operands : types . slice ( ) , } ) ; } let ret = types [ 0 ] ; if ( ! ret || typeof ret !== 'object' || ret . kind !== 'object' ) { throw new Error ( `First parameter of subtraction type should be 'object'.` ) ; } for ( const ty of types . slice ( 1 ) ) { if ( ty && typeof ty === 'object' && ty . kind === 'object' ) { ret = omit ( ret , ... ty . members . map ( m => m [ 0 ] ) ) ; } } return ret ; } export function primitive ( typeName ) { switch ( typeName ) { case 'never' : return ( { kind : 'never' , } ) ; case 'any' : return ( { kind : 'any' , } ) ; case 'unknown' : return ( { kind : 'unknown' , } ) ; case 'number' : case 'integer' : case 'bigint' : case 'string' : case 'boolean' : case 'null' : case 'undefined' : return ( { kind : 'primitive' , primitiveName : typeName , } ) ; case 'never?' : return ( optional ( { kind : 'never' , } ) ) ; case 'any?' : return ( optional ( { kind : 'any' , } ) ) ; case 'unknown?' : return ( optional ( { kind : 'unknown' , } ) ) ; case 'number?' : case 'integer?' : case 'bigint?' : case 'string?' : case 'boolean?' : case 'null?' : case 'undefined?' : return ( optional ( { kind : 'primitive' , primitiveName : typeName . substring ( 0 , typeName . length - 1 ) as any , } ) ) ; default : throw new Error ( `Unknown primitive type assertion: ${ typeName }` ) ; } } export function regexpPatternStringType ( pattern ) { return ( { kind : 'primitive' , primitiveName : 'string' , pattern , } ) ; } export function primitiveValue ( value ) { if ( value === null || value === void 0 ) { return ( { kind : 'primitive-value' , value , } ) ; } else switch ( typeof value ) { case 'number' : case 'bigint' : case 'string' : case 'boolean' : return ( { kind : 'primitive-value' , value , } ) ; default : throw new Error ( `Unknown primitive value assertion: ${ value }` ) ; } } export function optional ( ty ) { if ( ty && typeof ty === 'object' && ty . kind ) { if ( ty . kind === 'optional' ) { return ty ; } else { return ( { kind : 'optional' , optional : ty , ... ( ty . typeName ? { typeName : ty . typeName } : { } ) , } ) ; } } else { return ( { kind : 'optional' , optional : primitiveValue ( ty ) , } ) ; } } export function repeated ( ty , option ? ) { if ( ty && typeof ty === 'object' && ty . kind ) { return ( { kind : 'repeated' , min : option && typeof option . min === 'number' ? option . min : null , max : option && typeof option . max === 'number' ? option . max : null , repeated : ty , } ) ; } else { return ( { kind : 'repeated' , min : option && typeof option . min === 'number' ? option . min : null , max : option && typeof option . max === 'number' ? option . max : null , repeated : primitive ( ty ) , } ) ; } } export function sequenceOf ( ... seq ) { return ( { kind : 'sequence' , sequence : seq . map ( ty => ty && typeof ty === 'object' && ty . kind ? ty : primitiveValue ( ty ) ) , } ) ; } export function spread ( ty , option ? ) { if ( ty && typeof ty === 'object' && ty . kind ) { return ( { kind : 'spread' , min : option && typeof option . min === 'number' ? option . min : null , max : option && typeof option . max === 'number' ? option . max : null , spread : ty , } ) ; } else { return ( { kind : 'spread' , min : option && typeof option . min === 'number' ? option . min : null , max : option && typeof option . max === 'number' ? option . max : null , spread : primitiveValue ( ty ) , } ) ; } } export function enumType ( ... values ) { const ar = values . slice ( ) ; let value = 0 ; for ( let i = 0 ; i < ar . length ; i ++ ) { if ( isUnsafeVarNames ( dummyTargetObject , ar [ i ] [ 0 ] ) ) { throw new Error ( `Unsafe symbol name is appeared in enum assertion: ${ ar [ i ] [ 0 ] }` ) ; } if ( ar [ i ] [ 1 ] === null || ar [ i ] [ 1 ] === void 0 ) { ar [ i ] [ 1 ] = value ++ ; } else if ( typeof ar [ i ] [ 1 ] === 'number' ) { value = ( ar [ i ] [ 1 ] as number ) + 1 ; } if ( ! ar [ i ] [ 2 ] ) { ar [ i ] . length = 2 ; } } return ( { kind : 'enum' , values : ar as Array < [ string , number | string , string ? ] > , } ) ; } export function objectType ( ... members ) { const revMembers = members . slice ( ) . reverse ( ) ; for ( const x of members ) { if ( typeof x [ 0 ] === 'string' ) { if ( isUnsafeVarNames ( dummyTargetObject , x [ 0 ] ) ) { throw new Error ( `Unsafe symbol name is appeared in object assertion: ${ x [ 0 ] }` ) ; } if ( members . find ( m => m [ 0 ] === x [ 0 ] ) !== revMembers . find ( m => m [ 0 ] === x [ 0 ] ) ) { throw new Error ( `Duplicated member is found: ${ x [ 0 ] }` ) ; } } } const membersProps = ( members . filter ( x => typeof x [ 0 ] === 'string' ) as Array < [ string , PrimitiveValueTypes | TypeAssertion , string ? ] > ) . map ( x => x [ 1 ] && typeof x [ 1 ] === 'object' && x [ 1 ] . kind ? [ x [ 0 ] , withName ( x [ 1 ] , x [ 0 ] ) , x [ 2 ] ] : [ x [ 0 ] , withName ( primitiveValue ( x [ 1 ] ) , x [ 0 ] ) , x [ 2 ] ] ) . map ( x => ( x [ 2 ] ? [ x [ 0 ] , x [ 1 ] , false , ... x . slice ( 2 ) ] : [ x [ 0 ] , x [ 1 ] ] ) as ObjectAssertionMember ) ; const additionalProps = ( members . filter ( x => typeof x [ 0 ] !== 'string' ) as Array < [ AdditionalPropsKey , PrimitiveValueTypes | TypeAssertion , string ? ] > ) . map ( x => x [ 1 ] && typeof x [ 1 ] === 'object' && x [ 1 ] . kind ? x : [ x [ 0 ] , primitiveValue ( x [ 1 ] ) , x [ 2 ] ] ) . map ( x => ( x [ 2 ] ? [ x [ 0 ] , x [ 1 ] , false , ... x . slice ( 2 ) ] : [ x [ 0 ] , x [ 1 ] ] ) as AdditionalPropsMember ) ; return ( { ... { kind : 'object' , members : membersProps , } , ... ( 0 < additionalProps . length ? { additionalProps , } : { } ) , } ) ; } function checkRecursiveExtends ( ty , base ) { if ( ty === base ) { return false ; } if ( ty . typeName && ( ty . typeName === base . typeName || ( base . kind === 'symlink' && ty . typeName === base . symlinkTargetName ) ) ) { return false ; } if ( base . kind === 'object' && base . baseTypes ) { for ( const z of base . baseTypes ) { if ( ! checkRecursiveExtends ( ty , z ) ) { return false ; } } } return true ; } export function derived ( ty , ... exts ) { const ret = { kind : 'object' , members : [ ] , baseTypes : [ ] , } ; for ( const ext of exts ) { switch ( ext . kind ) { case 'object' : if ( ! checkRecursiveExtends ( ty , ext ) ) { throw new Error ( `Recursive extend is found: ${ ty . name || '(unnamed)' }` ) ; } for ( const m of ext . members ) { if ( ! ret . members . find ( x => x [ 0 ] === m [ 0 ] ) ) { ret . members . push ( [ m [ 0 ] , m [ 1 ] , true , ... m . slice ( 3 ) ] as ObjectAssertionMember ) ; } } case 'symlink' : ( ret . baseTypes as Array < ObjectAssertion | AssertionSymlink > ) . push ( ext ) ; break ; case 'operator' : { throw new Error ( `Unresolved type operator is found: ${ ext . operator }` ) ; } } } ret . members = ty . members . concat ( ret . members ) ; if ( ty . baseTypes ) { ret . baseTypes = ty . baseTypes . filter ( x => x . kind !== 'symlink' ) . concat ( ret . baseTypes as Array < ObjectAssertion | AssertionSymlink > ) ; } if ( ( ret . baseTypes as Array < ObjectAssertion | AssertionSymlink > ) . length === 0 ) { delete ret . baseTypes ; } const revMembers = ret . members . slice ( ) . reverse ( ) ; for ( const x of ret . members ) { if ( ret . members . find ( m => m [ 0 ] === x [ 0 ] ) !== revMembers . find ( m => m [ 0 ] === x [ 0 ] ) ) { throw new Error ( `Duplicated member is found: ${ x [ 0 ] } in ${ ty . name || '(unnamed)' }` ) ; } } let additionalProps = [ ] ; if ( ret . baseTypes ) { for ( const base of ret . baseTypes ) { if ( base . kind === 'object' ) { if ( base . additionalProps && 0 < base . additionalProps . length ) { additionalProps = additionalProps . concat ( base . additionalProps . map ( x => [ x [ 0 ] , x [ 1 ] , true , ... x . slice ( 3 ) ] as AdditionalPropsMember ) ) ; } } } } if ( ty . additionalProps && 0 < ty . additionalProps . length ) { additionalProps = additionalProps . concat ( ty . additionalProps ) ; } if ( 0 < additionalProps . length ) { ret . additionalProps = additionalProps ; } return ret ; } export function symlinkType ( name ) { return ( { kind : 'symlink' , symlinkTargetName : name , } ) ; } export function withName ( ty , name ) { if ( ! name ) { return ty ; } return ( { ... ty , name } ) ; } export function withTypeName ( ty , typeName ) { if ( ! typeName ) { return ty ; } return ( { ... ty , typeName } ) ; } export function withOriginalTypeName ( ty , originalTypeName ) { if ( ! originalTypeName ) { return ty ; } return ( { ... ty , originalTypeName } ) ; } export function withDocComment ( ty , docComment ) { if ( ! docComment ) { return ty ; } return ( { ... ty , docComment } ) ; } export function withRange ( minValue , maxValue ) { return ( ty ) => { if ( typeof minValue !== 'number' && typeof minValue !== 'string' ) { throw new Error ( `Decorator '@range' parameter 'minValue' should be number or string.` ) ; } if ( typeof maxValue !== 'number' && typeof maxValue !== 'string' ) { throw new Error ( `Decorator '@range' parameter 'maxValue' should be number or string.` ) ; } if ( ( ty as TypeAssertion ) . kind === 'optional' ) { const opt = ( ty as any as OptionalAssertion ) . optional ; if ( opt . kind !== 'primitive' ) { throw new Error ( `Decorator '@range' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , optional : { ... opt , minValue , maxValue } } ) ; } else { if ( ! ty || ty . kind !== 'primitive' ) { throw new Error ( `Decorator '@range' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , minValue , maxValue } ) ; } } ; } export function withMinValue ( minValue ) { return ( ty ) => { if ( typeof minValue !== 'number' && typeof minValue !== 'string' ) { throw new Error ( `Decorator '@minValue' parameter 'minValue' should be number or string.` ) ; } if ( ( ty as TypeAssertion ) . kind === 'optional' ) { const opt = ( ty as any as OptionalAssertion ) . optional ; if ( opt . kind !== 'primitive' ) { throw new Error ( `Decorator '@minValue' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , optional : { ... opt , minValue } } ) ; } else { if ( ! ty || ty . kind !== 'primitive' ) { throw new Error ( `Decorator '@minValue' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , minValue } ) ; } } ; } export function withMaxValue ( maxValue ) { return ( ty ) => { if ( typeof maxValue !== 'number' && typeof maxValue !== 'string' ) { throw new Error ( `Decorator '@maxValue' parameter 'maxValue' should be number or string.` ) ; } if ( ( ty as TypeAssertion ) . kind === 'optional' ) { const opt = ( ty as any as OptionalAssertion ) . optional ; if ( opt . kind !== 'primitive' ) { throw new Error ( `Decorator '@maxValue' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , optional : { ... opt , maxValue } } ) ; } else { if ( ! ty || ty . kind !== 'primitive' ) { throw new Error ( `Decorator '@maxValue' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , maxValue } ) ; } } ; } export function withGreaterThan ( greaterThanValue ) { return ( ty ) => { if ( typeof greaterThanValue !== 'number' && typeof greaterThanValue !== 'string' ) { throw new Error ( `Decorator '@greaterThan' parameter 'greaterThan' should be number or string.` ) ; } if ( ( ty as TypeAssertion ) . kind === 'optional' ) { const opt = ( ty as any as OptionalAssertion ) . optional ; if ( opt . kind !== 'primitive' ) { throw new Error ( `Decorator '@greaterThan' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , optional : { ... opt , greaterThanValue } } ) ; } else { if ( ! ty || ty . kind !== 'primitive' ) { throw new Error ( `Decorator '@greaterThan' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , greaterThanValue } ) ; } } ; } export function withLessThan ( lessThanValue ) { return ( ty ) => { if ( typeof lessThanValue !== 'number' && typeof lessThanValue !== 'string' ) { throw new Error ( `Decorator '@lessThan' parameter 'lessThan' should be number or string.` ) ; } if ( ( ty as TypeAssertion ) . kind === 'optional' ) { const opt = ( ty as any as OptionalAssertion ) . optional ; if ( opt . kind !== 'primitive' ) { throw new Error ( `Decorator '@lessThan' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , optional : { ... opt , lessThanValue } } ) ; } else { if ( ! ty || ty . kind !== 'primitive' ) { throw new Error ( `Decorator '@lessThan' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , lessThanValue } ) ; } } ; } export function withMinLength ( minLength ) { return ( ty ) => { if ( typeof minLength !== 'number' ) { throw new Error ( `Decorator '@minLength' parameter 'minLength' should be number.` ) ; } if ( ( ty as TypeAssertion ) . kind === 'optional' ) { const opt = ( ty as any as OptionalAssertion ) . optional ; if ( opt . kind !== 'primitive' ) { throw new Error ( `Decorator '@minLength' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , optional : { ... opt , minLength } } ) ; } else { if ( ! ty || ty . kind !== 'primitive' ) { throw new Error ( `Decorator '@minLength' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , minLength } ) ; } } ; } export function withMaxLength ( maxLength ) { return ( ty ) => { if ( typeof maxLength !== 'number' ) { throw new Error ( `Decorator '@maxLength' parameter 'maxLength' should be number.` ) ; } if ( ( ty as TypeAssertion ) . kind === 'optional' ) { const opt = ( ty as any as OptionalAssertion ) . optional ; if ( opt . kind !== 'primitive' ) { throw new Error ( `Decorator '@maxLength' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , optional : { ... opt , maxLength } } ) ; } else { if ( ! ty || ty . kind !== 'primitive' ) { throw new Error ( `Decorator '@maxLength' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , maxLength } ) ; } } ; } export function withMatch ( pattern ) { return ( ty ) => { if ( typeof pattern !== 'object' ) { throw new Error ( `Decorator '@match' parameter 'pattern' should be RegExp.` ) ; } if ( ( ty as TypeAssertion ) . kind === 'optional' ) { const opt = ( ty as any as OptionalAssertion ) . optional ; if ( opt . kind !== 'primitive' ) { throw new Error ( `Decorator '@match' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , optional : { ... opt , pattern } } ) ; } else { if ( ! ty || ty . kind !== 'primitive' || ty . primitiveName !== 'string' ) { throw new Error ( `Decorator '@match' cannot be applied to anything other than 'primitive'.` ) ; } return ( { ... ty , pattern } ) ; } } ; } export function withStereotype < T extends TypeAssertion > ( stereotype ) { if ( typeof stereotype !== 'string' ) { throw new Error ( `Decorator '@stereotype' parameter 'stereotype' should be string.` ) ; } if ( isUnsafeVarNames ( dummyTargetObject , stereotype ) ) { throw new Error ( `Unsafe symbol name is appeared in stereotype assertion: ${ stereotype }` ) ; } return ( ty ) => { if ( ty . kind === 'optional' ) { const ret = ( { ... ty , optional : { ... ( ty as OptionalAssertion ) . optional , stereotype , } , } ) ; return ret ; } else { const ret = ( { ... ty , stereotype , } ) ; return ret ; } } ; } export function withConstraint < T extends TypeAssertion > ( name , args ? ) { if ( typeof name !== 'string' ) { throw new Error ( `Decorator '@constraint' parameter 'name' should be string.` ) ; } if ( isUnsafeVarNames ( dummyTargetObject , name ) ) { throw new Error ( `Unsafe symbol name is appeared in constraint assertion: ${ name }` ) ; } return ( ty ) => { if ( ty . kind === 'optional' ) { const opt = ( ty as OptionalAssertion ) . optional ; const ret = ( { ... ty , optional : { ... opt , customConstraints : opt . customConstraints ? opt . customConstraints . slice ( ) . push ( name ) : [ name ] , customConstraintsArgs : opt . customConstraintsArgs ? { ... opt . customConstraintsArgs , [ name ] : args } : { [ name ] : args } , } , } ) ; return ret ; } else { const ret = ( { ... ty , customConstraints : ty . customConstraints ? ty . customConstraints . slice ( ) . push ( name ) : [ name ] , customConstraintsArgs : ty . customConstraintsArgs ? { ... ty . customConstraintsArgs , [ name ] : args } : { [ name ] : args } , } ) ; return ret ; } } ; } export function withForceCast < T extends TypeAssertion > ( ) { return ( ty ) => { if ( ty . kind === 'optional' ) { const ret = ( { ... ty , optional : { ... ( ty as OptionalAssertion ) . optional , forceCast : true , } , } ) ; return ret ; } else { const ret = ( { ... ty , forceCast : true , } ) ; return ret ; } } ; } export function withRecordType < T extends TypeAssertion > ( ) { return ( ty ) => { if ( ty . kind === 'optional' ) { const ret = ( { ... ty , optional : { ... ( ty as OptionalAssertion ) . optional , isRecordTypeField : true , } , } ) ; return ret ; } else { const ret = ( { ... ty , isRecordTypeField : true , } ) ; return ret ; } } ; } export function withMeta < T extends TypeAssertion > ( meta ) { return ( ty ) => { const ret = ( { ... ty , meta , } ) ; return ret ; } ; } export function withMsg < T extends TypeAssertion > ( messages ) { return ( ty ) => { if ( ty . kind === 'optional' ) { if ( typeof messages === 'string' ) { const ret = ( { ... ty , message : messages , optional : { ... ( ty as OptionalAssertion ) . optional , message : messages } , } ) ; delete ret . messages ; delete ret . optional . messages ; return ret ; } else { const ret = ( { ... ty , messages , optional : { ... ( ty as OptionalAssertion ) . optional , messages } , } ) ; delete ret . message ; delete ret . optional . message ; return ret ; } } else { if ( typeof messages === 'string' ) { const ret = ( { ... ty , message : messages } ) ; delete ret . messages ; return ret ; } else { const ret = ( { ... ty , messages } ) ; delete ret . message ; return ret ; } } } ; } export function withMsgId < T extends TypeAssertion > ( messageId ) { return ( ty ) => { if ( ty . kind === 'optional' ) { return ( { ... ty , messageId , optional : { ... ( ty as OptionalAssertion ) . optional , messageId } , } ) ; } else { return ( { ... ty , messageId } ) ; } } ; }