@@ -213,15 +213,6 @@ const pushAll = <A>(xs: Array<A>, ys: Array<A>): void => {
213213 }
214214}
215215
216- const getIsCodec = < T extends Any > ( tag : string ) => ( codec : Any ) : codec is T => ( codec as any ) . _tag === tag
217-
218- // tslint:disable-next-line: deprecation
219- const isAnyCodec = getIsCodec < AnyType > ( 'AnyType' )
220-
221- const isInterfaceCodec = getIsCodec < InterfaceType < Props > > ( 'InterfaceType' )
222-
223- const isPartialCodec = getIsCodec < PartialType < Props > > ( 'PartialType' )
224-
225216//
226217// basic types
227218//
@@ -999,21 +990,80 @@ export type OutputOfDictionary<D extends Any, C extends Any> = { [K in OutputOf<
999990export interface RecordC < D extends Mixed , C extends Mixed >
1000991 extends DictionaryType < D , C , { [ K in TypeOf < D > ] : TypeOf < C > } , { [ K in OutputOf < D > ] : OutputOf < C > } , unknown > { }
1001992
993+ function enumerableRecord < D extends Mixed , C extends Mixed > (
994+ keys : Array < string > ,
995+ domain : D ,
996+ codomain : C ,
997+ name : string = `{ [K in ${ domain . name } ]: ${ codomain . name } }`
998+ ) : RecordC < D , C > {
999+ const len = keys . length
1000+ return new DictionaryType (
1001+ name ,
1002+ ( u ) : u is { [ K in TypeOf < D > ] : TypeOf < C > } => UnknownRecord . is ( u ) && keys . every ( k => codomain . is ( u [ k ] ) ) ,
1003+ ( u , c ) =>
1004+ chain ( UnknownRecord . validate ( u , c ) , o => {
1005+ const a : { [ key : string ] : any } = { }
1006+ const errors : Errors = [ ]
1007+ let changed : boolean = false
1008+ for ( let i = 0 ; i < len ; i ++ ) {
1009+ const k = keys [ i ]
1010+ const ok = o [ k ]
1011+ const codomainResult = codomain . validate ( ok , appendContext ( c , k , codomain , ok ) )
1012+ if ( isLeft ( codomainResult ) ) {
1013+ pushAll ( errors , codomainResult . left )
1014+ } else {
1015+ const vok = codomainResult . right
1016+ changed = changed || vok !== ok
1017+ a [ k ] = vok
1018+ }
1019+ }
1020+ return errors . length > 0 ? failures ( errors ) : success ( ( changed || Object . keys ( o ) . length !== len ? a : o ) as any )
1021+ } ) ,
1022+ codomain . encode === identity
1023+ ? identity
1024+ : ( a : any ) => {
1025+ const s : { [ key : string ] : any } = { }
1026+ for ( let i = 0 ; i < len ; i ++ ) {
1027+ const k = keys [ i ]
1028+ s [ k ] = codomain . encode ( a [ k ] )
1029+ }
1030+ return s as any
1031+ } ,
1032+ domain ,
1033+ codomain
1034+ )
1035+ }
1036+
10021037/**
1003- * @since 1.7.1
1038+ * @internal
10041039 */
1005- export const record = < D extends Mixed , C extends Mixed > (
1040+ export function getDomainKeys < D extends Mixed > ( domain : D ) : Record < string , unknown > | undefined {
1041+ if ( isLiteralC ( domain ) ) {
1042+ const literal = domain . value
1043+ if ( string . is ( literal ) ) {
1044+ return { [ literal ] : null }
1045+ }
1046+ } else if ( isKeyofC ( domain ) ) {
1047+ return domain . keys
1048+ } else if ( isUnionC ( domain ) ) {
1049+ const keys = domain . types . map ( type => getDomainKeys ( type ) )
1050+ return keys . some ( undefinedType . is ) ? undefined : Object . assign ( { } , ...keys )
1051+ }
1052+ return undefined
1053+ }
1054+
1055+ function nonEnumerableRecord < D extends Mixed , C extends Mixed > (
10061056 domain : D ,
10071057 codomain : C ,
10081058 name : string = `{ [K in ${ domain . name } ]: ${ codomain . name } }`
1009- ) : RecordC < D , C > => {
1059+ ) : RecordC < D , C > {
10101060 return new DictionaryType (
10111061 name ,
10121062 ( u ) : u is { [ K in TypeOf < D > ] : TypeOf < C > } => {
10131063 if ( UnknownRecord . is ( u ) ) {
10141064 return Object . keys ( u ) . every ( k => domain . is ( k ) && codomain . is ( u [ k ] ) )
10151065 }
1016- return isAnyCodec ( codomain ) && Array . isArray ( u )
1066+ return isAnyC ( codomain ) && Array . isArray ( u )
10171067 } ,
10181068 ( u , c ) => {
10191069 if ( UnknownRecord . is ( u ) ) {
@@ -1044,7 +1094,7 @@ export const record = <D extends Mixed, C extends Mixed>(
10441094 }
10451095 return errors . length > 0 ? failures ( errors ) : success ( ( changed ? a : u ) as any )
10461096 }
1047- if ( isAnyCodec ( codomain ) && Array . isArray ( u ) ) {
1097+ if ( isAnyC ( codomain ) && Array . isArray ( u ) ) {
10481098 return success ( u )
10491099 }
10501100 return failure ( u , c )
@@ -1066,6 +1116,16 @@ export const record = <D extends Mixed, C extends Mixed>(
10661116 )
10671117}
10681118
1119+ /**
1120+ * @since 1.7.1
1121+ */
1122+ export function record < D extends Mixed , C extends Mixed > ( domain : D , codomain : C , name ?: string ) : RecordC < D , C > {
1123+ const keys = getDomainKeys ( domain )
1124+ return keys
1125+ ? enumerableRecord ( Object . keys ( keys ) , domain , codomain , name )
1126+ : nonEnumerableRecord ( domain , codomain , name )
1127+ }
1128+
10691129/**
10701130 * @since 1.0.0
10711131 */
@@ -1628,9 +1688,9 @@ const stripKeys = (o: any, props: Props): unknown => {
16281688}
16291689
16301690const getExactTypeName = ( codec : Any ) : string => {
1631- if ( isInterfaceCodec ( codec ) ) {
1691+ if ( isTypeC ( codec ) ) {
16321692 return `{| ${ getNameFromProps ( codec . props ) } |}`
1633- } else if ( isPartialCodec ( codec ) ) {
1693+ } else if ( isPartialC ( codec ) ) {
16341694 return getPartialTypeName ( `{| ${ getNameFromProps ( codec . props ) } |}` )
16351695 }
16361696 return `Exact<${ codec . name } >`
@@ -2109,14 +2169,27 @@ function intersectTags(a: Tags, b: Tags): Tags {
21092169 return r
21102170}
21112171
2172+ // tslint:disable-next-line: deprecation
2173+ function isAnyC ( codec : Any ) : codec is AnyC {
2174+ return ( codec as any ) . _tag === 'AnyType'
2175+ }
2176+
21122177function isLiteralC ( codec : Any ) : codec is LiteralC < LiteralValue > {
21132178 return ( codec as any ) . _tag === 'LiteralType'
21142179}
21152180
2181+ function isKeyofC ( codec : Any ) : codec is KeyofC < Record < string , unknown > > {
2182+ return ( codec as any ) . _tag === 'KeyofType'
2183+ }
2184+
21162185function isTypeC ( codec : Any ) : codec is TypeC < Props > {
21172186 return ( codec as any ) . _tag === 'InterfaceType'
21182187}
21192188
2189+ function isPartialC ( codec : Any ) : codec is PartialC < Props > {
2190+ return ( codec as any ) . _tag === 'PartialType'
2191+ }
2192+
21202193// tslint:disable-next-line: deprecation
21212194function isStrictC ( codec : Any ) : codec is StrictC < Props > {
21222195 return ( codec as any ) . _tag === 'StrictType'
0 commit comments