Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / ES6

Learn with Me - TypeScript: Doing Things with Functions

4.78/5 (6 votes)
5 Apr 2016CPOL7 min read 16.6K   42  
A deeper dive in TypeScript with the discussion around functions and how to work with them

Previously in "Learn with Me - TypeScript"

Introduction

In the previous article, we have had an introduction to TypeScript and a discussion around its type system. In this article, we will take a look at the functions. We will discuss how to define and work with functions and what TypeScript allows us to do with them.

How to Use the Code

It is expected that an editor and TypeScript compiler is installed in your PC and you know how to compile a typescript file. If you do not have those in place, do not panic. Go to the first part of the series, A Study in Types.

TypeScript Function Basics

Like in JavaScript, TypeScript also allows you to create functions by declaration or by expression.

Defining a Function

Function declaration means defining a function with function keyword and a valid function name. The function will be accessible by that name. On the other hand, expressed function, not necessarily have a name, is stored in a variable, and the function can be accessed by the variable name.

JavaScript
function declaredFunction () {
}

var expressedFunction = function () {
}

Functions not having a name are called anonymous functions.

Apart from these, TypeScript gives us another way to define a function even without the function keyword. We can use the arrow operator (=>) instead.

JavaScript
var arrowOperatorExp = () => {
}

Type Notation

Just because its name is TypeScript, just like for the variables, it allows us to have type notation for the functions as well. We can define static type notation for a function by its return type.

To start with, let's create a TypeScript file in our working folder, named functions_type-notation.ts and put the following code snippet in.

JavaScript
function myFuncDecl1 (): void {
}

var myFuncExp1 = function (): void {
}

var arrowOperatorExp = (): void => {
}

Compile it and have a look into the .js file. Now, add the following lines and try to compile it again.

JavaScript
function myFuncDecl2 (): void {
    return true;
}

The compilation error indicates that the compiler does a validation on the return type of the function and the static notation for the function's return type. Let's delete that function and go forward.

TypeScript also allow us to define static type notations for the parameters.

JavaScript
function sayHello(name: string): void{
}

function saySomething(name: string, num: number): void{
}

In TypeScript, when a function is called, it looks for an exact signature with parameter types exactly in the same order as the argument types. Being governed by the rule, unlike JavaScript, once you define static types for the parameters, you cannot call the function with arguments that do not match with the types of the parameters.

JavaScript
sayHello(2);
saySomething(2, "The World");

Both of these lines will raise compilation errors as none of them match with any of the function signatures.

Optional Parameters

In JavaScript, each and every parameter in a function is implicitly an optional parameter. But TypeScript does not provide us with that luxury, which is good. But even if it is good, sometimes the situation demands for parameters to be optional in a function. So, TypeScript allows us to have explicit optional parameters. Wow, how cool is that!!! Developers now have more control not only over the functions but also, on the invocations of the functions.

To continue with this discussion, we will need another TypeScript file named "functions_optional-parameters.ts". Type in the following snippet, and try to compile.

JavaScript
function addNumbersWithOptionalParams(a: number, b: number, c?:number): void {
    var res = a + b;

    if(c)
    {
        res += c;

        alert('a + b + c = ' + res);
        return;
    }

    alert('a + b = ' + res);
}
addNumbersWithOptionalParams(1);
addNumbersWithOptionalParams(1, 2);
addNumbersWithOptionalParams(1, 2, 3);

The first attempt to invoke the method addNumbersWithOptionalParams() will cause a compilation error, but the other two calls to the function will not. If we comment out the problematic line, and compile, it will be compiled nicely.

One thing to remember though. While developing a function with optional parameters, they are required to be included after the mandatory parameters in the function signature. In case there are multiple optional parameters, we can use pass arguments for selective parameters. We can exclude passing arguments for the parameters at the end of the function signature. But, if we need to skip some from the middle, we will need to pass either null or undefined for those. For example, If we have a function signature as:

JavaScript
function addAndOptionalMultiply (a: number, b: number, c?:number, m?:number): void {};

To use a, b and m, we need to call it as below:

JavaScript
addAndOptionalMultiply (1, 2, null, 3);

Default Parameters

If we define a function with some functional parameters, we need to check if the parameters contain any value or not before using those parameters. Wouldn't it be nice to have some mechanism where we can set a default value to the optional parameters if the caller does not pass any value for them? Yes, besides having optional parameters, TypeScript also allows us to have default parameters. Let's create a new file named "functions_default-parameters.ts". Type in the following snippet and compile:

JavaScript
function addNumbersWithDefaultParameters(a: number, b: number, c:number = 0, d: number = 0): void {
    var res = a + b + c + d;

    alert('result = ' + res);
}

addNumbersWithDefaultParameters(1, 2);
addNumbersWithDefaultParameters(1, 2, 3)

Because we have used the default parameters in our function, we don't have to check if the optional variable are undefined or not. It comes in handy when we need to change the signature of an existing function with more parameters. Let's have a look at the output JavaScript file as well to have a better understanding about the default parameters.

JavaScript
function addNumbersWithDefaultParameters(a, b, c, d) {
    if (c === void 0) { c = 0; }
    if (d === void 0) { d = 0; }
    var res = a + b + c + d;
    alert('result = ' + res);
}
addNumbersWithDefaultParameters(1, 2);
addNumbersWithDefaultParameters(1, 2, 3);

Rest Parameters

We have learned about the optional parameters and we have learned about the default parameters. Both of these have one problem in common - the limitation for passing the arguments. We cannot pass six arguments for a function which takes only four parameters. But not to worry, we have rest parameters to support that. An elipsis before the parameter name and using an array as the type will do the work.

Create a new file named "functions_rest-parameters.ts". Put the following code snippet in and compile.

JavaScript
function addNumbersWithRestParameters(... n: number[]): void {
    var result = 0;
    var i;

    for(i=0; i < n.length; i++) {
        result += n[i];
    }

    alert("Result = " + result);
}

addNumbersWithRestParameters(2);
addNumbersWithRestParameters(2, 2, 2, 2, 2);

Have a look at the JavaScript file:

JavaScript
function addNumbersWithRestParameters() {
    var n = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        n[_i - 0] = arguments[_i];
    }
    var result = 0;
    var i;
    for (i = 0; i < n.length; i++) {
        result += n[i];
    }
    alert("Result = " + result);
}
addNumbersWithRestParameters(2);
addNumbersWithRestParameters(2, 2, 2, 2, 2);

Interestingly, there is no parameter in the function. So, how is it receiving the arguments? If you are not familiar with the arguments object in JavaScript, it is a built in object, local to the function which behaves mostly like an array. When a function is called, the arguments object holds all the arguments and can be accessed from the function.

As you see, though it looks slick in the TypeScript, it introduces a redundant loop for assigning the items from the arguments object to a local variable, hence has a performance issue.

Function Overloading

Function overloading is possible in TypeScript. If you don't know what I am talking about, function overloading is the capability of having more than one functions with the same name but different signature (with different types or different number of parameters).

Just like always, we will write some code. Let's take a file named "functions_overloading.ts" in our working folder. Type the following code in and compile.

JavaScript
function print (n: number);
function print (str: string);
function print (b: boolean);
function print (v: number | string | boolean) {
    if(typeof(v) === "number")
    {
        alert(v + " is a number")
    }
    else if(typeof(v) === "string")
    {
        alert("'" + v + "' is a string")
    }
    else if(typeof(v) === "boolean")
    {
        alert("'" + v + "' is a boolean")
    }
    else {
        alert("error");
    }
}

print(1);
print("Hello World!");
print(true);

Have a look at the output JavaScript file. Doesn't it seem that we overworked to write the TypeScript, it would be lot easier to write a JavaScript function instead? Well, it yes but here is the benefit - the print() function in the JavaScript is allowed to accept any argument and possibly will often end up with the error. On the other hand, because, TypeScript is typed, you cannot pass any argument with types other than number, string and bool. You can give it a try by adding the following line:

JavaScript
print({id: 1, name: "test"});

Callbacks

Like JavaScript, callbacks are allowed to be passed as arguments in TypeScript. And as everything else, they can be typed as well. Let's see how. Create a new file named "functions_callbacks.ts" and fill it up with the following lines.

JavaScript
var mainFunction = function(callback: () => void){
    alert("Main Function");
    callback();
}

var callbackFunction = function(): void {
    alert("Callback");
}

mainFunction(callbackFunction);

When done, save and compile. Easy, we have a main function that accepts any void callback functions and during its execution, it executes the callback. The "Arrow" function definition comes into good use while working with callbacks.

JavaScript
mainFunction((): void => {
    alert("Arrow Function Callback");
})

Immediately Invoked Function

As we learned from the previous episode, A Study in Types, that var variables are scoped to be local to the function. Immediately Invoked Function is a design pattern that uses this principle to create smaller scopes for variables with limited usage. Create a new file named "functions_iif.ts" and type in the following lines:

JavaScript
function iif_main(): void {
    var gVar: number = 1;

    (function(v: number){
        gVar = v;
        var lVar: number = v;

        alert("gVar inside IIF=" + gVar);
        alert("lVar inside IIF=" + lVar);
    })(2);

    alert("gVar in mainFunction = " + gVar);
    alert("lVar in mainFunction = " + lVar); //Error
}

iif_main();

Compile. lVar, declared in the immediately invoked function is not available in the outer function's scope.

Conclusion

Now that we know about the basics of the functions, we can enter the object oriented world of TypeScript. But that will be another story.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)