Blog-Archiv

Mittwoch, 13. Juni 2018

TypeScript Function Types

TypeScript (TS) interfaces can describe function signatures. The thing implementing such an interface must be callable like a function, so it must be a function. A "callable interface", hard to imagine for an OO-thinking Java developer, actually exists in TS:

interface SearchFunction
{
    (text: string, pattern: string): boolean;
}

const search: SearchFunction = function(text: string, pattern: string): boolean {
    return text.search(pattern) >= 0;
}

const contained = search("ABCDEF", "EF");
console.assert(contained === true, "Pattern 'EF' is expected to be found in 'ABCDEF'");

The constant search is typed to the interface SearchFunction, and assigned to be a function that has the signature described in the interface. The subsequent test shows that this actually works.

The distinction between function and object (interface) is becoming quite diffuse here. Most likely this is another TS feature just to integrate already existing JavaScript APIs. From the TS language specification:

An object type containing one or more call signatures is said to be a function type.
Function types may be written using function type literals or by including call signatures in object type literals.

The TS Handbook speaks about "Hybrid Types". A TS interface can describe a mix of function and object, and if you also include index signatures it would be a mix of function, object and array!

Terms

Following terms might be worth to learn:

Call Signature
(text: string, pattern: string): boolean
Function Type
interface SearchFunction
{
    (text: string, pattern: string): boolean;
}
Function Type Literal
(text: string, pattern: string) => boolean

Yes, it's not a typo, right of the arrow is the return type!
Using a function type literal, we could type above search() function, without interface, like this:

const search: 
    (text: string, pattern: string) => boolean = 
    function(text: string, pattern: string): boolean {
        return text.search(pattern) >= 0;
    }

Or even like that:

const search: 
    (text: string, pattern: string) => boolean = 
    (text: string, pattern: string): boolean => {
        return text.search(pattern) >= 0;
    }

Baffled? I think freedom of expression in context of computer languages undermines human common sense. Murphy's Law stands by: what can be misunderstood will be misunderstood! Just count the minutes you need to understand this code.

Conclusion

Here is a mix of object, function, and array, all in one interface.
The <JackOfAllTrades> in line 13 is a cast-operator (mind that there are two kinds of casts in TS, the other one would be an as JackOfAllTrades in line 15).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
interface JackOfAllTrades
{
    (): string;  // function signature
    
    [shotInTheFoot: string]: any;  // index signature
    
    index: number;  // object-property
    
    contains(text: string, pattern: string): boolean;  // object-method
}

function newJackOfAllTrades(): JackOfAllTrades {
    const jack = <JackOfAllTrades> function(): string {
        return "Jack";
    };
    
    jack["Hello"] = "World";
    
    jack.index = 1;
    
    jack.contains = function(text: string, pattern: string): boolean {
        return text.search(pattern) >= 0;
    };
    
    return jack;
}

const jack = newJackOfAllTrades();

console.assert(jack() ===  "Jack", "jack() is expected to return 'Jack'");
console.assert(jack["Hello"] === "World", "value at 'Hello' is expected to be 'World'");
console.assert(jack.index === 1, "index is expected to be 1");
console.assert(jack.contains("ABC", "B"), "'ABC' is expected to contain 'B'");

This is not only syntactically correct, but also all assertions are successful! Whether it is useful for everyday programming may be doubted.




Keine Kommentare: