Search code examples
typescriptbounded-types

Lower bounded parameters in TypeScript


I'm fairly new to TypeScript so I may well just be doing something stupid but I've found what appears to be either a bug or a limitation. I have defined an interface for a function that takes a Foo. If I give that function to another function, the contract would seem to be that the function I pass must take any Foo. However, if I define class Bar extends Foo and then a function that takes a Bar, but not a Foo, I can pass that to a function that requires a function taking any Foo. Example:

class Foo {

}

class Bar extends Foo {

}

interface TakesAnyFoo {
    (x: Foo) : void;
}

function needsAFunctionThatTakesAnyFoo(f: TakesAnyFoo) {
    let foo = new Foo();
    f(foo);
}

function takesOnlyABar(x: Bar) {
    console.log(`I got ${x}`);
}

needsAFunctionThatTakesAnyFoo(takesOnlyABar);

I think maybe what I need here is the equivalent of Java's lower bounded types. Something like:

interface TakesAnyFoo<T super Foo> {
    (x: T) : void;
}

but Typescript doesn't appear to have this. How can I enforce a constraint that the function passed to needsAFunctionThatTakesAnyFoo must accept any Foo as its input?


Solution

  • Simplifying your code we have :

    class Foo {
    }
    
    class Bar extends Foo {
    }
    
    function needsAFunctionThatTakesAnyFoo(f: (x: Foo) => void) {
        let foo = new Foo();
        f(foo);
    }
    
    needsAFunctionThatTakesAnyFoo((x: Bar) => { });
    

    This is supported to allow the common pattern in event handlers. e.g. Giving a function that takes a MouseEvent to an event specification that only supports Event.

    More

    This is covered here:

    https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Type%20Compatibility.md#comparing-two-functions