# ObjectDecoder overview

This namespace contains object decoders and additional utilities for object validations.

# Summary

# Functions

# ObjectDecoder.dict

# Description

Check if the input is an record, where all properties are of the given type.

<A>(decoder: Decoder<unknown, A>) => Decoder<unknown, any>

# ObjectDecoder.struct

# Description

Check if the input is an object, where all object properties match the given decoders. All extraenous properties will be skipped and ignored.

<A extends any>(props: Struct<A>, name?: string | undefined) => ObjectDecoder<unknown, A>

# Example

const TodoDto = ObjectDecoder.struct({
  id: IntegerDecoder.positive,
  title: TextDecoder.varchar(1, 100),
  done: BooleanDecoder.boolean
})

const input: unknown = {
  id: 0,
  title: 'Wake up',
  description: 'Some description', // This property is not recognized by the decoder and will be ignored.
  done: false
}

expect(pipe(input, Decoder.validate(TodoDto), Result.isOk)).toBe(true)
expect(pipe(input, Decoder.validate(TodoDto), Result.get)).toEqual({
  id: 0,
  title: 'Wake up',
  done: false
})

# ObjectDecoder.omit

# Description

Omit given properties from an ObjectDecoder. The resulting ObjectDecoder will not contain the omitted properties.

<I, O extends any, B extends keyof O>(props: Array<B>) => (decoder: ObjectDecoder<I, O>) => ObjectDecoder<I, Omit<O, B>>

# Example

const TodoPostDto = pipe(TodoDto, ObjectDecoder.omit(['id']))

const input: unknown = {
  id: 0, // This property has been omitted and will be ignored.
  title: 'Wake up',
  done: false
}

expect(pipe(input, Decoder.validate(TodoPostDto), Result.isOk)).toBe(true)
expect(pipe(input, Decoder.validate(TodoPostDto), Result.get)).toEqual({
  title: 'Wake up',
  done: false
})

# ObjectDecoder.pick

# Description

Pick given properties from an ObjectDecoder. The resulting ObjectDecoder will only contain the picked properties.

<I, O extends any, B extends keyof O>(props: Array<B>) => (decoder: ObjectDecoder<I, O>) => ObjectDecoder<I, Pick<O, B>>

# Example

const TodoPostDto = pipe(TodoDto, ObjectDecoder.pick(['title', 'done']))

const input: unknown = {
  id: 0, // This property has not been picked and will be ignored.
  title: 'Wake up',
  done: false
}

expect(pipe(input, Decoder.validate(TodoPostDto), Result.isOk)).toBe(true)
expect(pipe(input, Decoder.validate(TodoPostDto), Result.get)).toEqual({
  title: 'Wake up',
  done: false
})

# ObjectDecoder.partial

# Description

Make all properties of an ObjectDecoder optional.

<I, O extends any>(decoder: ObjectDecoder<I, O>) => ObjectDecoder<I, Partial<O>>

# ObjectDecoder.guard

# Description

Add a custom validation function to the ObjectDecoder.

<I, O extends any>(fn: (input: O) => any) => (decoder: ObjectDecoder<I, O>) => ObjectDecoder<unknown, any>

# Example

const SignupDto = pipe(
  ObjectDecoder.struct({
    email: TextDecoder.email,
    password: TextDecoder.varchar(5, 50),
    passwordRepeat: TextDecoder.varchar(5, 50)
  }),
  ObjectDecoder.guard(user => {
    return user.password === user.passwordRepeat
      ? undefined
      : DecodeError.object([
        DecodeError.key('passwordRepeat', DecodeError.value(user.passwordRepeat, `Password does not match password confirmation`))
      ])
  })
)

const input: unknown = {
  email: 'test@example.com',
  password: '12345',
  passwordRepeat: '12345'
}

expect(pipe(input, Decoder.validate(SignupDto), Result.isKo)).toBe(true)

# ObjectDecoder.merge

# Description

Merge multiple ObjectDecoders. If a property has already been declared, the decoder for this property will be overwritten.

<I, O1 extends any>(a: ObjectDecoder<I, O1>): ObjectDecoder<I, O1>
<I, O1 extends any, O2 extends any>(a: ObjectDecoder<I, O1>, b: ObjectDecoder<I, O2>): ObjectDecoder<I, O2 & Omit<O1, keyof O2>>
<I, O1 extends any, O2 extends any, O3 extends any>(a: ObjectDecoder<I, O1>, b: ObjectDecoder<I, O2>, c: ObjectDecoder<I, O3>): ObjectDecoder<I, O3 & Omit<O2, keyof O3> & Omit<O1, keyof O3 | keyof O2>>
<I, O1 extends any, O2 extends any, O3 extends any, O4 extends any>(a: ObjectDecoder<I, O1>, b: ObjectDecoder<I, O2>, c: ObjectDecoder<I, O3>, d: ObjectDecoder<I, O4>): ObjectDecoder<I, O4 & Omit<O3, keyof O4> & Omit<O2, keyof O4 | keyof O3> & Omit<O1, keyof O4 | keyof O3 | keyof O2>>

# Example

const A = ObjectDecoder.struct({
  a: TextDecoder.string,
  ab: TextDecoder.string
})
const B = ObjectDecoder.struct({
  ab: NumberDecoder.number,
  b: NumberDecoder.number
})
const C = ObjectDecoder.merge(A, B)

interface C extends Decoder.TypeOf<typeof C> {}

// Interface C is now equals to:
type C = {
  a: string
  ab: number
  b: number
}

// Note that "ab: TextDecoder.string" has been overwritten and will not be executed

# ObjectDecoder.additionalProperties

# Description

By default, ObjectDecoder.struct will skip and ignore extra properties. If you wish to keep extra properties (all properties not validated by the struct), you can use this util.

<I, O extends any>(decoder: ObjectDecoder<I, O>) => Decoder<I, any>

# Example

const Response = pipe(
  ObjectDecoder.struct({
    status: EnumDecoder.literal('OK', 'KO'),
    message: TextDecoder.string,
  }),
  ObjectDecoder.additionalProperties
)

# ObjectDecoder.sum

# Description

Execute a specific decoder depending on the value of a given "type" property.

<K extends string, I, T extends any>(prop: K, cases: T) => Decoder<I, SumTypes<K, T>>

# Example

const Geom = ObjectDecoder.sum('type', {
  Circle: struct({
    radius: NumberDecoder.number
  }),
  Rectangle: struct({
    width: NumberDecoder.number,
    height: NumberDecoder.number
  })
})

type Geom = Decoder.TypeOf<typeof Geom>
const geom: Geom = {
  type: 'Rectangle',
  height: 5
}