import { expectType , TypeEqual } from "ts-expect" ; import { boolean , DecoderError , fields , fieldsAuto , fieldsUnion , multi , number , optional , string , stringUnion , } from ".." ; test ( "making a type from a decoder" , ( ) => { const personDecoder1 = fields ( ( field ) => ( { name : field ( "name" , string ) , age : field ( "age" , number ) , } ) ) ; const personDecoder1Auto = fieldsAuto ( { name : string , age : number , } ) ; type Person1 = ReturnType < typeof personDecoder1 > ; expectType < TypeEqual < Person1 , { name : string ; age : number ; } > > ( true ) ; type Person1Auto = ReturnType < typeof personDecoder1Auto > ; expectType < TypeEqual < Person1Auto , { name : string ; age : number ; } > > ( true ) ; const testPerson = { name : "John" , age : 30 } ; function meet ( personA , personB ) { return `${ personA . name } meets ${ personB . name }` ; } meet ( personDecoder1 ( testPerson ) , personDecoder1Auto ( testPerson ) ) ; const userDecoder = fieldsAuto ( { id : multi ( { string , number } ) , name : string , age : number , active : boolean , country : optional ( string ) , type : stringUnion ( { user : null } ) , } ) ; type User = ReturnType < typeof userDecoder > ; const data = { id : 1 , name : "John Doe" , age : 30 , active : true , type : "user" , } ; const user = userDecoder ( data ) ; expect ( user ) . toMatchInlineSnapshot ( `
    Object {
      "active": true,
      "age": 30,
      "country": undefined,
      "id": 1,
      "name": "John Doe",
      "type": "user",
    }
  ` ) ; const user2 = { id : 1 , name : "John Doe" , age : 30 , active : true , country : undefined , type : "nope" , } ; expect ( { ... user2 , type : "user" } ) . toMatchObject ( user ) ; const user3 = { id : 1 , name : "John Doe" , age : 30 , active : true , country : undefined , type : "user" , extra : "prop" , } ; expect ( user3 ) . toMatchObject ( user ) ; const userDecoder2 = fields ( ( field ) => ( { id : field ( "id" , multi ( { string , number } ) ) , name : field ( "name" , string ) , age : field ( "age" , number ) , active : field ( "active" , boolean ) , country : field ( "country" , optional ( string ) ) , type : field ( "type" , stringUnion ( { user : null } ) ) , } ) ) ; type User2 = ReturnType < typeof userDecoder2 > ; const user4 = { id : 1 , name : "John Doe" , age : 30 , active : true , country : undefined , type : "nope" , } ; expect ( { ... user4 , type : "user" } ) . toMatchObject ( user ) ; } ) ; test ( "making a type from a decoder – unions" , ( ) => { const userDecoder1 = fieldsUnion ( "type" , { anonymous : fieldsAuto ( { type : ( ) => "anonymous" as const , sessionId : number , } ) , registered : fieldsAuto ( { type : ( ) => "registered" as const , id : number , name : string , } ) , } ) ; type InferredType1 = ReturnType < typeof userDecoder1 > ; type ExpectedType1 = { type : "anonymous" ; sessionId : number ; } | { type : "registered" ; id : number ; name : string ; } ; expectType < TypeEqual < InferredType1 , ExpectedType1 > > ( true ) ; const userDecoder2 = fields ( ( field ) => { const type = field ( "type" , string ) ; switch ( type ) { case "anonymous" : return { type : "anonymous" , sessionId : field ( "sessionId" , number ) , } ; case "registered" : return { type : "registered" , id : field ( "id" , number ) , name : field ( "name" , string ) , } ; default : throw new DecoderError ( { message : "Unknown user type" , value : type , key : "type" , } ) ; } } ) ; type InferredType2 = ReturnType < typeof userDecoder2 > ; type ActualType2 = { type : string ; id : number ; name : string ; sessionId ? : undefined ; } | { type : string ; sessionId : number ; id ? : undefined ; name ? : undefined ; } ; expectType < TypeEqual < InferredType2 , ActualType2 > > ( true ) ; const userDecoder3 = fields ( ( field ) => { const type = field ( "type" , string ) ; switch ( type ) { case "anonymous" : return { type : "anonymous" as const , sessionId : field ( "sessionId" , number ) , } ; case "registered" : return { type : "registered" as const , id : field ( "id" , number ) , name : field ( "name" , string ) , } ; default : throw new DecoderError ( { message : "Unknown user type" , value : type , key : "type" , } ) ; } } ) ; type InferredType3 = ReturnType < typeof userDecoder3 > ; type ActualType3 = { type : "anonymous" ; sessionId : number ; id ? : undefined ; name ? : undefined ; } | { type : "registered" ; id : number ; name : string ; sessionId ? : undefined ; } ; expectType < TypeEqual < InferredType3 , ActualType3 > > ( true ) ; const testUser = userDecoder3 ( { type : "anonymous" , sessionId : 123 } ) ; if ( testUser . type === "anonymous" ) { } const id = < T > ( x ) : T => x ; const userDecoder4 = fields ( ( field ) => { const type = field ( "type" , string ) ; switch ( type ) { case "anonymous" : return id ( { type : "anonymous" as const , sessionId : field ( "sessionId" , number ) , } ) ; case "registered" : return id ( { type : "registered" as const , id : field ( "id" , number ) , name : field ( "name" , string ) , } ) ; default : throw new DecoderError ( { message : "Unknown user type" , value : type , key : "type" , } ) ; } } ) ; type InferredType4 = ReturnType < typeof userDecoder4 > ; type ActualType4 = { type : "anonymous" ; sessionId : number ; } | { type : "registered" ; id : number ; name : string ; } ; expectType < TypeEqual < InferredType4 , ActualType4 > > ( true ) ; function getUserDecoder ( type ) { switch ( type ) { case "anonymous" : return fields ( ( field ) => ( { type : "anonymous" as const , sessionId : field ( "sessionId" , number ) , } ) ) ; case "registered" : return fieldsAuto ( { type : ( ) => "registered" as const , id : number , name : string , } ) ; default : throw new DecoderError ( { message : "Unknown user type" , value : type , key : "type" , } ) ; } } const userDecoder5 = fields ( ( field , obj , errors ) => field ( "type" , getUserDecoder ) ( obj , errors ) ) ; type InferredType5 = ReturnType < typeof userDecoder5 > ; type ActualType5 = { type : "anonymous" ; sessionId : number ; } | { type : "registered" ; id : number ; name : string ; } ; expectType < TypeEqual < InferredType5 , ActualType5 > > ( true ) ; expect ( userDecoder5 ( { type : "anonymous" , sessionId : 1 } ) ) . toMatchInlineSnapshot ( `
      Object {
        "sessionId": 1,
        "type": "anonymous",
      }
    ` ) ; } ) ;