import fc from 'fast-check' ; import { isA , typeCheckFor } from 'ts-type-checked' ; describe ( 'special-cases' , ( ) => { const circularTypeError = /^Value that was passed to ts-type-checked contains a circular reference and cannot be checked$/ ; describe ( 'same-type circular structure' , ( ) => { type TypeReference1 = { next ? : TypeReference1 ; } ; const createLinkedList = ( n , close ) : [ TypeReference1 , TypeReference1 ] => { const head = { } as TypeReference1 ; let tail = head ; for ( let i = 0 ; i < n ; i ++ ) { tail = tail . next = { } as TypeReference1 ; } if ( close ) tail . next = head ; return [ head , tail ] ; } ; test ( 'same-type circular structure should throw an error' , ( ) => { const circularArbitrary = fc . integer ( 0 , 100 ) . map < TypeReference1 > ( ( n ) => createLinkedList ( n , true ) [ 0 ] ) ; fc . assert ( fc . property ( circularArbitrary , ( value ) => { expect ( ( ) => typeCheckFor < TypeReference1 > ( ) ( value ) ) . toThrow ( circularTypeError ) ; expect ( ( ) => isA < TypeReference1 > ( value ) ) . toThrow ( circularTypeError ) ; } ) as any ) ; } ) ; test ( 'recovering after same-type circular structure check' , ( ) => { const circularArbitrary = fc . integer ( 0 , 50 ) . map < [ TypeReference1 , TypeReference1 ] > ( ( n ) => createLinkedList ( n , true ) ) ; fc . assert ( fc . property ( circularArbitrary , ( [ head , tail ] ) => { expect ( ( ) => typeCheckFor < TypeReference1 > ( ) ( head ) ) . toThrow ( circularTypeError ) ; expect ( ( ) => isA < TypeReference1 > ( head ) ) . toThrow ( circularTypeError ) ; head . next = undefined ; expect ( typeCheckFor < TypeReference1 > ( ) ( head ) ) . toBeTruthy ( ) ; expect ( isA < TypeReference1 > ( head ) ) . toBeTruthy ( ) ; head . next = tail ; expect ( ( ) => typeCheckFor < TypeReference1 > ( ) ( head ) ) . toThrow ( circularTypeError ) ; expect ( ( ) => isA < TypeReference1 > ( head ) ) . toThrow ( circularTypeError ) ; } ) as any ) ; } ) ; } ) ; describe ( 'alternating-type circular structure' , ( ) => { type TypeReference1 = { next ? : TypeReference2 ; } ; type TypeReference2 = { following ? : TypeReference1 ; } ; const createLinkedList = ( n , close ) : [ TypeReference1 , TypeReference2 ] => { const head = { } as TypeReference1 ; let tail = head ; for ( let i = 0 ; i < n ; i ++ ) { tail . next = { } as TypeReference2 ; tail = tail . next . following = { } as TypeReference1 ; } if ( close ) { tail . next = { } as TypeReference2 ; tail . next . following = head ; } return [ head , tail . next ! ] ; } ; test ( 'alternating-type circular structure should throw an error' , ( ) => { const circularArbitrary = fc . integer ( 0 , 100 ) . map < TypeReference1 > ( ( n ) => createLinkedList ( n , true ) [ 0 ] ) ; fc . assert ( fc . property ( circularArbitrary , ( value ) => { expect ( ( ) => typeCheckFor < TypeReference1 > ( ) ( value ) ) . toThrow ( circularTypeError ) ; expect ( ( ) => isA < TypeReference1 > ( value ) ) . toThrow ( circularTypeError ) ; expect ( ( ) => typeCheckFor < TypeReference2 > ( ) ( value . next ) ) . toThrow ( circularTypeError ) ; expect ( ( ) => isA < TypeReference2 > ( value . next ) ) . toThrow ( circularTypeError ) ; } ) as any ) ; } ) ; test ( 'recovering after same-type circular structure check' , ( ) => { const circularArbitrary = fc . integer ( 0 , 50 ) . map < [ TypeReference1 , TypeReference2 ] > ( ( n ) => createLinkedList ( n , true ) ) ; fc . assert ( fc . property ( circularArbitrary , ( [ head , tail ] ) => { expect ( ( ) => typeCheckFor < TypeReference1 > ( ) ( head ) ) . toThrow ( circularTypeError ) ; expect ( ( ) => isA < TypeReference1 > ( head ) ) . toThrow ( circularTypeError ) ; tail . following = undefined ; expect ( typeCheckFor < TypeReference1 > ( ) ( head ) ) . toBeTruthy ( ) ; expect ( isA < TypeReference1 > ( head ) ) . toBeTruthy ( ) ; tail . following = head ; expect ( ( ) => typeCheckFor < TypeReference1 > ( ) ( head ) ) . toThrow ( circularTypeError ) ; expect ( ( ) => isA < TypeReference1 > ( head ) ) . toThrow ( circularTypeError ) ; } ) as any ) ; } ) ; } ) ; } ) ;