Is this binary search in TypeScript correct? I need your help to see if the following binary search code is correct. I did my best to cover the corner cases.  I wonder if I missed anything.

The code as it is with tests (you can play with it online here):

function binarySearch&lt;a, r&gt;(
    hay: a[],
    needle: a,
    compare:(one: a, another: a, oneGreaterThanAnother: () =&gt; r, oneLessThanAnother: () =&gt; r, oneEqualToAnother: () =&gt; r) =&gt; r,
    haveStaightHit: (hay: a[], index: number) =&gt; r,
    haveExpectedToBeAt: (hay: a[], index: number) =&gt; r
) {
    if (hay.length &gt; 0) {
        return binarySearchUnsafe(hay, needle, 0, hay.length - 1, compare, haveStaightHit, haveExpectedToBeAt);
    } else {
        return haveExpectedToBeAt(hay, 0);
    }
}
function binarySearchUnsafe&lt;a, r&gt;(
    hay: a[],
    needle: a,
    from: number,
    to: number,
    compare:(one: a, another: a, oneGreaterThanAnother: () =&gt; r, oneLessThanAnother: () =&gt; r, oneEqualToAnother: () =&gt; r) =&gt; r,
    haveStaightHit: (hay: a[], index: number) =&gt; r,
    haveExpectedToBeAt: (hay: a[], index: number) =&gt; r
): r {
    if (from &lt; to) {
        var at = from + (~~((to - from) / 2));
        return compare(
            needle,
            hay[at],
            function needleIsOnTheRight() {
                return binarySearchUnsafe(hay, needle, at + 1, to, compare, haveStaightHit, haveExpectedToBeAt);
            },
            function needleIsOnTheLeft() {
                return binarySearchUnsafe(hay, needle, from, at, compare, haveStaightHit, haveExpectedToBeAt);
            },
            function needleIsFound() {
                return haveStaightHit(hay, at);
            }
        );      
    } else if (from &gt; to) {
        throw new Error('From index ' + from + ' is bigger than the to index ' + to + '.');
    } else {
        var at = from /* === to */;
        return compare(
            needle,
            hay[at],
            function needleIsOnTheRight() {
                return haveExpectedToBeAt(hay, at + 1);
            },
            function needleIsOnTheLeft() {
                return haveExpectedToBeAt(hay, at);             
            },
            function needleIsFound() {
                return haveStaightHit(hay, at)
            }
        );
    }
}

function areEqual(actual: any, expected: any) {
    if (actual !== expected) throw new Error('Expected: ' + expected + ', Actual: ' + actual);
}

function compareStrings&lt;r&gt;(one: string, another: string, oneGreaterThanAnother: () =&gt; r, oneLessThanAnother: () =&gt; r, oneEqualToAnother: () =&gt; r) : r {
    if (one &gt; another) {
        return oneGreaterThanAnother();
    } else if (one &lt; another) {
        return oneLessThanAnother();
    } else {
        return oneEqualToAnother();
    }
}

function failOver(message: string) : any {
    return function() {
        throw new Error(message);
    }
}

try {
    binarySearch([], 'x', compareStrings, failOver('Direct hit is not expected.'), (hay, index) =&gt; areEqual(index, 0));
    binarySearch(['b', 'd', 'f'], 'b', compareStrings, (hay, index) =&gt; areEqual(index, 0), failOver('Direct hit is expected.'));
    binarySearch(['b', 'd', 'f'], 'd', compareStrings, (hay, index) =&gt; areEqual(index, 1), failOver('Direct hit is expected.'));
    binarySearch(['b', 'd', 'f'], 'f', compareStrings, (hay, index) =&gt; areEqual(index, 2), failOver('Direct hit is expected.'));
    binarySearch(['b', 'd', 'f'], 'g', compareStrings, failOver('Direct hit is not expected.'), (hay, index) =&gt; areEqual(index, 3));
    binarySearch(['b', 'd', 'f'], 'a', compareStrings, failOver('Direct hit is not expected.'), (hay, index) =&gt; areEqual(index, 0));
    binarySearch(['b', 'd', 'f'], 'c', compareStrings, failOver('Direct hit is not expected.'), (hay, index) =&gt; areEqual(index, 1));
    alert('You are all good!');
} catch (e) {
    alert(e.message);
}
That looks jolly complicated.  If you haven't read it, look for Jon Bentley's Programming Pearls article on binary search.  Here's my version:

var bSearch = &lt;T&gt;(xs: T[], x: T, cmp: (p: T, q: T) =&gt; number): number =&gt; {
    var bot = 0;
    var top = xs.length;
    while (bot &lt; top) { // If x is in xs, it's somewhere in xs[bot..top).
        var mid = Math.floor((bot + top) / 2);
        var c = cmp(xs[mid], x);
        if (c === 0) return mid;
        if (c &lt; 0) bot = mid + 1;
        if (0 &lt; c) top = mid; 
    }
    return -1;
}

var cmp = (p: number, q: number) =&gt; p - q;

console.log(bSearch([], 3, cmp) === -1);
console.log(bSearch([3], 3, cmp) === 0);
console.log(bSearch([1, 3], 3, cmp) === 1);
console.log(bSearch([3, 4], 3, cmp) === 0);
console.log(bSearch([3, 3], 3, cmp) !== -1);
console.log(bSearch([1, 2, 3], 3, cmp) === 2);
console.log(bSearch([1, 3, 4], 3, cmp) === 1);
console.log(bSearch([3, 4, 5], 3, cmp) === 0);
console.log(bSearch([4, 5, 6], 3, cmp) === -1);