Implementing a generic Comparator I'm familiar with JS, however just today started TypeScript. Experimenting with the type system and generics, I thought to implement a comparator yielding 3 distinct results and created the following:

export function compare&lt;T&gt;(s1: T, s2: T): number {
    let equal = 0;
    let greater = 1;
    let lesser = -1

    // Special cases: consider "undefined" and "null" to be less than defined
    if (nullOrUndefined(s1) &amp;&amp; nullOrUndefined(s2)) {
        return equal;
    } else if (nullOrUndefined(s1) ^ nullOrUndefined(s2)) {
        return nullOrUndefined(s2) ? greater : lesser;
    }

    // Ignore case for strings
    if (typeof s1 === 'string') {
        s1 = s1.toLowerCase();
        s2 = s2.toLowerCase();
    }

    // Use JS default
    return s1 === s2 ? equal : s1 &gt; s2 ? greater : lesser;
}

export function nullOrUndefined(input: any): boolean {
    return input === undefined || input === null;
}


My questions are:


Are the variables necessary? I keep going back and forth on this / wonder if it aids clarity in any way. Sometimes feel like it's intuitive, but I even wonder if an enumeration representing comparison results outside could be superior.
My first implementation used the any type, but it feels more appropriate to apply it this way since it should always compare like-items. This feels like a sensical assumption, but would any prove more handy and 'generic' in any case?
For any unfamiliar with JS weirdness, see this article.
Personally, because this function can be simplified, I don't believe the variables are necessary. Since you are using Typescript, an enum would fit well here, but I believe even this is unnecessary. The results of a compare function are well standardized.

enum Result {
  equal = 0,
  greater = 1,
  lesser = -1
}


You are correct that it is more appropriate to apply the function with generics. If somewhere you need the flexibility that any provides, you can simply call the function with compare&lt;any&gt;(a, b) to override the type. I personally have found that any is only useful when first converting a project and will avoid it like the plague if at all possible.

Now, a few comments about the function itself. 


I'm not sure how you are compiling this, but it emits three errors in the playground.


nullOrUndefined(s1) ^ nullOrUndefined(s2) - The '^' operator is not allowed for boolean types. Consider using '!==' instead.
s1 = s1.toLowerCase();- Type 'string' is not assignable to type 'T'.
s2 = s2.toLowerCase(); - Property 'toLowerCase' does not exist on type 'T'.

You will have problems if you try to use this function with the built in Array.prototype.sort function because undefined results in the array being treated as a sparse array. See the spec.
To ignore string case, it would be better to use String.prototype.localeCompare. 


Here is how I would implement this function. I purposely use == instead of === for comparing null as it also catches undefined.

function compare&lt;T&gt;(a: T, b: T): number {
    if (a == b) return 0;
    if (a == null) return -1;
    if (b == null) return 1;

    // To appease the type system, check both
    // I think it might be a bug that this is required
    if (typeof a === 'string' &amp;&amp; typeof b === 'string') {
        return a.localeCompare(b, 'standard', { sensitivity: 'case'});
    }

    return b &gt; a ? -1 : 1;
}