import { Component , ReactNode } from 'react' ; type XOR < T , U > = ( T | U ) extends object ? ( Without < T , U > & U ) | ( Without < U , T > & T ) : T | U ; type Without < T , U > = { [ P in Exclude < keyof T , keyof U > ] ? : never ; } ; type Props = { onToggle : ( on : boolean ) => void ; } & RenderProps ; type RenderProps = XOR < { children : ( api : API ) => ReactNode ; } , { render : ( api : API ) => ReactNode ; } > ; type API = ReturnType < Toggle [ 'getApi' ] > ; type State = Readonly < typeof initialState > ; const initialState = { on : false } ; export class Toggle extends Component < Props , State > { readonly state = initialState ; private toggle = ( ) => this . setState ( ( { on } ) => ( { on : ! on } ) , ( ) => this . props . onToggle ( this . state . on ) ) ; private getApi ( ) { return { on : this . state . on , toggle : this . toggle , } ; } render ( ) { if ( hasRender ( this . props ) ) { return this . props . render ( this . getApi ( ) ) ; } if ( hasChildren ( this . props ) ) { return this . props . children ( this . getApi ( ) ) ; } throw new Error ( 'children is mandatory and needs to be a function!' ) ; } } type HasRenderProp < T > = T extends { render : ( props : any ) => ReactNode ; } ? T : never ; type HasChildrenProp < T > = T extends { children : ( props : any ) => ReactNode ; } ? T : never ; type IsFunction < T > = T extends ( ... args : any [ ] ) => any ? T : never ; const hasRender = < T extends { } > ( value ) : value is HasRenderProp < T > => 'render' in value && isFunction ( ( value as HasRenderProp < T > ) . render ) ; const hasChildren = < T extends { } > ( value ) : value is HasChildrenProp < T > => 'children' in value && isFunction ( ( value as HasChildrenProp < T > ) . children ) ; const isFunction = < T extends { } > ( value ) : value is IsFunction < T > => typeof value === 'function' ;