Welcome to pay attention to my public account “Life Code”

Front-end to advanced graphic tutorials, super detailed Web front-end learning notes. Learn front-end from scratch and be a refined and elegant front-end engineer. Author of public account “Life code”.

A website focused on share beauty diagram, an interesting web site, a back-end management system, small program a web site, search beautiful pictures Confirm the user is male or female image preview function Thumb up details users, comments, collection Can subscribe to other people’s photo album You can create photo albums Home page, the user center, create photo albums

Feel free to like, follow and comment

Translated from

TypeScript tutorial for beginners: who this guide is for

TypeScript Tutorial: Who is this guide for

The following guide is a TypeScript tutorial for JavaScript developers interested in learning more about TypeScript. This means that you need to know enough about “normal” JavaScript, although I’ll give you some basic guidance along the way.

Do the word TypeScript and “beginner” belong in the same tutorial? I wasn’t sure before writing this guide, but every day I see a lot of beginners getting interested in TypeScript. If you decide to do this, be aware that learning TypeScript and JavaScript at the same time is difficult in your early days. But in the long run, it’s worth it. Keep going! If that’s your case, you’re welcome to read on.

Before you start, make sure you have the latest version of Node.js installed on your system.

Now enjoy reading!

What is TypeScript?

The definition on the official website reads: “Typed superset of JavaScript,” but it assumes you know what “superset” is and what “typed” means. For simplicity, you can think of TypeScript as the “top layer” of JavaScript.

TypeScript is a layer because you can write TypeScript code in your editor. When compiled, everything in TypeScript is gone, and simple JavaScript is left.

If the concept of a compile step confuses you, remember that JavaScript is compiled and explained. There is a JavaScript engine that reads and executes your code.

But JavaScript engines can’t read TypeScript code, so any TypeScript file goes through a “pre-translation” process, that is, compilation. Only after the first compilation step is the pure JavaScript code left, ready to run in the browser. You’ll see how TypeScript compiles later.

For now, it’s important to remember that TypeScript is a special type of JavaScript, but it needs a “translator” before it can run in a browser.

Why TypeScript?

At first, you won’t fully understand why TypeScript makes sense; after all, it was stripped away before it became JavaScript code. “What’s TypeScript good for?” you ask. That’s a good question, my friend.

In fact, as long as it catches serious and stupid errors in your code, you’ll see the benefits. More importantly, your code base will become well-structured and almost self-documenting. You’ll also appreciate the improved autocomplete feature in the editor, but that’s just a nice side effect.

Anyway, every now and then a new post pops up on Twitter or the “Orange site” saying TypeScript is useless (TypeScript tax) or too embarrassing.

Partisans were almost on either side of the barricade. TypeScript has its pros and cons, but the bottom line is that TypeScript is a reliable tool, and it won’t hurt to put it on your tool belt.

My goal is to showcase the tool and help you form your own view of TypeScript.

TypeScript tutorial for Beginners: Set up TypeScript

Settings? Why is that? Isn’t TypeScript just a language? Kind of. TypeScript also has a binary code that compiles TypeScript code into JavaScript code. Remember, browsers don’t understand TypeScript. So, let’s install the binaries. Create a new node project in a new folder:

mkdir typescript-tutorial && cd $_
npm init -y
Copy the code

Then install TypeScript as follows:

npm i typescript --save-dev
Copy the code

Next, configure a node script so that we can easily run the TypeScript compiler:

"scripts": {
    "tsc": "tsc"
  },
Copy the code

TSC stands for TypeScript compiler, and when the compiler runs, it looks for a file named tsconfig. Json is in the project folder. Let’s generate a configuration file for TypeScript:

npm run tsc -- --init
Copy the code

If all goes well, you get “message TS6071: TsConfig was created successfully. You will see the new file in the project folder. Now, stay calm. Tsconfig. Json is a horrible configuration file. You don’t need to know every single point of it. In the next section, you’ll see the relevant parts of getting started.

Configure the TypeScript compiler

It is a good idea to initialize a Git repo and commit the raw tsConfig. Before touching the file. We will keep only some configuration options and remove all others. Later, you may want to compare your version to the original version. To start opening tsconfig.json and replace all the original content with the following:

{
  "compilerOptions": {
    "target": "es5"."strict": true}}Copy the code

Save and close the file. First, you might want to know what tsConfig is. Json. This configuration file is read by the TypeScript compiler and any typescript-enabled code editor.

TypeScript compiles to “plain” JavaScript. The key goal is to determine the required version of JavaScript, ES5(or the latest version).

This depends on the “rigor” of tsConfig. If you don’t add the appropriate type comments to your code, the compiler and editor will follow this rule (more on this later).

When strict is true, TypeScript enforces the maximum level of type checking in your code:

NoImplicitAny True: TypeScript reports an error when a variable has no type defined

Always sstrict true: Strict mode is a JavaScript security mechanism that prevents unexpected global variables, defaults to this binding, and so on. When always sstrict is set to true, TypeScript issues “Use Strict” at the top of every JavaScript file.

There are more configuration options available. You’ll learn more over time, and for now these two options are all you need to know to get started. But what does “any” mean?

A few words about types

Now you know what TypeScript does. It’s all about genre. They are not typical JavaScript “types” such as String, Object, Boolean. TypeScript adds more types of its own, just like any(or more).

Any is a “loose” TypeScript type. This means: this variable can be any type: string, Boolean, object, really, I don’t care. This is really like having no type checking at all. When strict is set to true, you say to TypeScript, “No ambiguity in my code.”

For this reason, I recommend keeping TypeScript as strict as possible, even if fixing all errors is difficult at first. Now we’re almost ready to see TypeScript in action!

TypeScript tutorial for Beginners: What TypeScript does

Everything starts with the legal (obviously)JavaScript function filterByTerm. Create a new file called filterbyter.js in your project folder and copy the following code into it:

function filterByTerm(input, searchTerm) {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("inputArr cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

filterByTerm("input string"."java");
Copy the code

Don’t worry if you don’t see the logic right now. After a few lines, let’s look at the parameters of this function and how they are used. Just by looking at the code, you should have spotted the problem (and no, it’s not Java).

I was wondering if there was a way to check this function in my IDE without having to run the code or test it using Jest. Is that possible? TypeScript does this very well; in fact, it’s one of the best tools in JavaScript for static checking, that is, “testing” your code for correctness before it runs.

So go into the TypeScript world and change the file extension from Filterbyter.js to FilterbyTer.ts. With this change, you’ll find a bunch of bugs in your code:

Can you see the red mark under the function argument? From now on, I’ll show you errors in text, but keep in mind that ides and text editors display red lines when you make errors in TypeScript.

To confirm what we did wrong, run:

npm run tsc
Copy the code

Look at these mistakes:

ilterByTerm.ts:1:23 - error TS7006: Parameter 'input' implicitly has an 'any' type.

filterByTerm.ts:1:30 - error TS7006: Parameter 'searchTerm' implicitly has an 'any' type.

filterByTerm.ts:5:32 - error TS7006: Parameter 'arrayElement' implicitly has an 'any' type.
Copy the code

TypeScript is telling you that function parameters have type any, which can be any type in TypeScript, if you remember. We need to add appropriate type annotations to our TypeScript code.

Wait, what exactly is kata?

What are types, and what’s wrong with JavaScript?

JavaScript has types, if you used the language before you knew there were strings, booleans, numbers, objects, and so on. As of today, there are eight types of JavaScript:

  • String
  • Number
  • BigInt
  • Boolean
  • Null
  • Undefined
  • Object
  • Symbol

Everything in the list is a “primitive”, except Object, which is a type. Each JavaScript type has a corresponding representation that we can use in our code, such as strings and numbers:

var name = "Hello John";
var age = 33;
Copy the code

The “problem” with JavaScript is that a variable can change its type anytime it (or we) want. For example, a Boolean value can later become a string (save the following code in a file named types.js):

var aBoolean = false;
console.log(typeof aBoolean); // "boolean"

aBoolean = "Tom";
console.log(typeof aBoolean); // "string"
Copy the code

The conversion can be intentional, and the developer may really want to assign “Tom” to aBoolean, but such errors are most likely accidental.

Now, technically, there’s nothing wrong with JavaScript per se, because its “type dynamics” are intentional. JavaScript was born as a simple Web scripting language, not as a full-fledged enterprise language.

However, JavaScript relaxation naturally causes serious problems in your code and undermines its maintainability. TypeScript aims to solve these problems by adding reinforcement types to JavaScript. In fact, if you change the types.js extension to types. You’ll see TypeScript complaining in ides.

Compiling types.ts yields:

types.ts:4:1 - error TS2322: Type '"Tom"' is not assignable to type 'boolean'.

Copy the code

Dabble in TypeScript types

TypeScript revolves around types, and our code seems to have no types at all. It’s time to add some. We first need to determine the function parameters. By looking at how the function is called, you can see that it takes two strings as arguments:

filterByTerm("input string"."java");
Copy the code

Are we sure? Let’s add our first type annotation to the function. The method is as follows:

function filterByTerm(input: string, searchTerm: string) {
    // omitted
}

// omitted
Copy the code

That’s it! By adding types to parameters, we migrate our code from pure JavaScript to TypeScript. But if you try to compile code:

npm run tsc
Copy the code

What happened:

filterByTerm.ts:5:16 - error TS2339: Property 'filter' does not exist on type 'string'.

Copy the code

Can you see how TypeScript guides you? The problem is the filter function:

function filterByTerm(input: string, searchTerm: string) {
    // omitted
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}
Copy the code

We tell TypeScript that “input” is a string, but later in the code we call the filter method, which is an array. What we really want is to mark “input” as an array, maybe an array of strings, okay?

To do this, you have two options. Option 1 with string[]:

function filterByTerm(input: string[], searchTerm: string) {
    // omitted
}
Copy the code

Or, if you like this syntax, option 2 Array

:

function filterByTerm(input: Array<string>, searchTerm: string) {
    // omitted

}
Copy the code

I personally prefer the second option. Now let’s try compiling again (NPM runs TSC), here it is:

filterByTerm.ts:10:14 - error TS2345: Argument of type '"input string"' is not assignable to parameter of type 'string[]'.

filterByTerm("input string"."java");
Copy the code

We mark the input as an array of strings, and now we try to pass in a string. It’s easy to fix! Let’s pass an array of strings:

filterByTerm(["string1"."string2"."string3"]."java");

Copy the code

Here’s the full code so far:

function filterByTerm(input: Array<string>, searchTerm: string) {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

filterByTerm(["string1"."string2"."string3"]."java");
Copy the code

I think it’s good. But not if you compile it (NPM runs TSC):

filterByTerm.ts:6:25 - error TS2339: Property 'url' does not exist on type 'string'.

Copy the code

Okay, TypeScript, good. We pass in an array of strings, but later in the code we try to access a property called “URL” :

return arrayElement.url.match(regex);
Copy the code

Beginner TypeScript tutorial :TypeScript objects and interfaces

TypeScript complains because filterByTerm is passed to an array of strings. Url “property does not have TypeScript type string. Let’s help TypeScript by passing an array of objects, each of which has the required URL property:

filterByTerm(
  [{ url: "string1" }, { url: "string2" }, { url: "string3"}]."java"
);
Copy the code

While you’re there, update the function signature to accept an array of objects:

function filterByTerm(input: Array<object>, searchTerm: string) {
    // omitted
}
Copy the code

Now let’s compile the code:

npm run tsc
Copy the code

happen

filterByTerm.ts:6:25 - error TS2339: Property 'url' does not exist on type 'object'.

Copy the code

Here we go again! This makes sense, at least in TypeScript: normal JavaScript objects don’t have any property called “URL.” For me, this is where TypeScript really starts to shine.

The point is, you can’t just assign attributes to a random object and call it a day. TypeScript requires that every entity in your code conform to a specific shape. This shape has a name in TypeScript :interface.

Now, it may seem like strange syntax at first, but once you get used to interfaces, you’ll start using them everywhere. But what is an interface? Interfaces in TypeScript are like contracts. In other words, interfaces are like “models” of entities.

Looking at our code, we can think of a simple “model”, named Link, where the shape of the object should conform to the following pattern:

It must have a URL property of type string

In TypeScript, you can define this “model” with an interface like this (put the following code at the top of filterbyter.ts:

interface Link {
  url: string;
}
Copy the code

In the interface declaration, we say, “From now on, I want to use this shape in my TypeScript code.” Of course, this is not valid JavaScript syntax and will be removed during compilation.

We can now use our interface by modifying the “input” parameter, which is also a custom TypeScript type:

function filterByTerm(input: Array<Link>, searchTerm: string) {
    // omitted
}
Copy the code

In this fix, we tell TypeScript to “expect a Link array” as input to this function. Here is the complete code:

interface Link {
  url: string;
}

function filterByTerm(input: Array<Link>, searchTerm: string) {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

filterByTerm(
  [{ url: "string1" }, { url: "string2" }, { url: "string3"}]."java"
);
Copy the code

Now all errors should disappear and you can run:

npm run tsc
Copy the code

The compilation step will generate a file in the project folder called filterbyter.js that contains pure JavaScript code. You can check out this file to see how TypeScript specific declarations are removed.

Because “always sstrict “is set to true, the TypeScript compiler also issues” Use strict” at the top of filterbyter.js.

Well done for your first TypeScript code! In the next section, we’ll explore interfaces further.

TypeScript Tutorial: Interfaces and Fields

The TypeScript interface is one of the most powerful constructs of the language. Interfaces help form a “model” in your application so that any developer can choose that model and follow it when writing code.

So far, we have defined a simple interface Link:

interface Link {
  url: string;
}
Copy the code

If you want to add more fields to the interface, you need to declare them in a block:

interface Link {
  description: string;
  id: number;
  url: string;
}
Copy the code

Now any object of type Link must “implement” the new field, or an error will occur. In fact, by compiling the code:

npm run tsc
Copy the code

TypeScript yells at you:

filterByTerm.ts:17:4 - error TS2739: Type '{ url: string; } ' is missing the following properties from type 'Link': description, id
Copy the code

The problem is the parameters of the function:

filterByTerm(
  [{ url: "string1" }, { url: "string2" }, { url: "string3"}]."java"
);
Copy the code

TypeScript looks at function declarations to infer that parameters are of type Array of Link. Therefore, any object in the array must have all the fields defined in the interface link (implementation).

In most cases, this is far from ideal. After all, we don’t know if every new object of type Link will have all the fields. Don’t worry, to make the compilation pass, we can declare the interface fields optional with a question mark:

interface Link { description? : string; id? : number; url: string; }Copy the code

TypeScript Tutorial: Variable types

So far, you have seen how to add types to function parameters:

function filterByTerm(input: Array<Link>, searchTerm: string) {
    //
}
Copy the code

TypeScript isn’t limited to that; of course, you can add a type to any variable. To illustrate this concept, let’s extract the parameters of the function one by one. First, I’ll extract each object:

const obj1: Link = { url: "string1" };
const obj2: Link = { url: "string2" };
const obj3: Link = { url: "string3" };
Copy the code

Notice how I tell TypeScript obj1, obj2, and obj3 are of type Link. In “Vanilla” JavaScript you would write:

const obj1 = { url: "string1" };
const obj2 = { url: "string2" };
const obj3 = { url: "string3" };
Copy the code

Next, we can define a Link array like this:

const arrOfLinks: Array<Link> = [obj1, obj2, obj3];
Copy the code

Finally, search terms:

const term: string = "java";
Copy the code

The final complete code:

interface Link { description? : string; id? : number; url: string; }function filterByTerm(input: Array<Link>, searchTerm: string) {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

const obj1: Link = { url: "string1" };
const obj2: Link = { url: "string2" };
const obj3: Link = { url: "string3" };

const arrOfLinks: Array<Link> = [obj1, obj2, obj3];

const term: string = "java";

filterByTerm(arrOfLinks, term);
Copy the code

Okay, I understand. Compared to JavaScript, TypeScript can seem wordy and sometimes redundant. But over time, you’ll find that the more types you add, the more robust your code becomes.

The more you help TypeScript understand the intent of your code by adding type annotations, the better off you’ll be. Your development experience will skyrocket.

For example, now that arrOfLinks is associated with the correct type (Link’s array), the editor can infer that each object in the array has a property called URL, as defined in the Link interface:

Now tell me it’s not great, because it is. TypeScript has many other types besides strings, arrays, and numbers.

There are booleans, tuples, “any”, never, enumerations. In time, you’ll learn it all. If you’re curious, check out the basic type of documentation.

Now let’s continue to extend the interface.

(For the most part, Typescript can infer types on its own. As a rule of thumb, let it work for you!)

TypeScript Tutorial: Extending interfaces

TypeScript interfaces are great. However, there will come a time when you need to add a new entity to your code that happens to be almost identical to another existing interface. For example, we want a new interface named TranslatedLink with the following properties:

  • id, number
  • url, string
  • description, string
  • language, string

Description, ID and URL… It looks like we already have the Link interface with the same properties:

interface Link { description? : string; id? : number; url: string; }Copy the code

Is there a way to reuse interface links? It turns out that in TypeScript, you can extend interfaces by assigning attributes to new interfaces; TranslatedLink, for example, “inherits” features from Link. Here’s how to do this, noting the keyword extends:

interface Link { description? : string; id? : number; url: string; } interface TranslatedLinkextends Link {
  language: string;
}
Copy the code

Now, any object of type TranslatedLink will have the optional properties Description, ID, URL, and the new property language:

interface Link { description? : string; id? : number; url: string; } interface TranslatedLinkextends Link {
  language: string;
}

const link1: TranslatedLink = {
  description:
    "TypeScript tutorial for beginners is a tutorial for all the JavaScript developers ...".id: 1.url: "www.valentinog.com/typescript/".language: "en"
};
Copy the code

When an object such as link1 uses an interface, we say link1 implements the properties defined in that interface. On the other hand, an interface has an implementation when it is used to describe one or more objects in code.

Extending an interface means borrowing its properties and extending them for code reuse. But wait, there’s more! You’ll soon see that TypeScript interfaces can also describe functions.

But first let’s look at the index!

TypeScript Tutorial: Indexing interludes

JavaScript objects are containers for key/value pairs. Imagine a simple object:

const paolo = {
  name: "Paolo".city: "Siena".age: 44
};
Copy the code

We can access the value of any key using point syntax:

console.log(paolo.city);
Copy the code

Or use the parenthesis syntax (JavaScript arrays are also true, since arrays are a special kind of object):

console.log(paolo["city"]);
Copy the code

Now, suppose the key becomes dynamic so that we can put it in a variable and reference it in parentheses:

const paolo = {
  name: "Paolo".city: "Siena".age: 44
};

const key = "city";

console.log(paolo[key]);
Copy the code

Now, let’s add another object, place both objects in an array, and use the filter method to filter the array as in filterbyter.js. But this time the key is passed dynamically, so it can be filtered by any key:

const paolo = {
  name: "Paolo".city: "Siena".age: 44
};

const tom = {
  name: "Tom".city: "Munich".age: 33
};

function filterPerson(arr, term, key) {
  return arr.filter(function(person) {
    return person[key].match(term);
  });
}

filterPerson([paolo, tom], "Siena"."city");
Copy the code

Relevant content is as follows:

return person[key].match(term);
Copy the code

Can you work? Yes, because JavaScript doesn’t care if Paolo or Tom are “indexable” with dynamic keys. What about TypeScript? Does it give an error in this case?

Let’s see: In the next section, we’ll use mutable keys to make filterByTerm more dynamic.

Interfaces can have indexes

Let’s go back to filterByTerm. Specifically, the filterByTerm function:

function filterByTerm(input: Array<Link>, searchTerm: string) {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}
Copy the code

It seems less flexible, because for each link, we match the hard-coded property “URL” with the regular expression. We might want the properties, the keys, to be dynamic. Here’s the first attempt:

function filterByTerm(
  input: Array<Link>,
  searchTerm: string,
  lookupKey: string = "url"
) {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement[lookupKey].match(regex);
  });
}
Copy the code

LookupKey is a dynamic key, which is also assigned a default parameter as a fallback, the string “URL.” Let’s compile the code:

npm run tsc
Copy the code

An error

error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Link'. No index signature with a parameter of type 'string' was found on type 'Link'.
Copy the code

Here’s the offending line:

return arrayElement[lookupKey].match(regex);
Copy the code

“No index signature”. Wow. This is an “easy” fix. Head over the interface Link and add index:

interfaceLink { description? :string; id? :number;
  url: string;
  [index: string] :string;
}
Copy the code

The syntax is a bit strange, but similar to dynamic key access on objects. This means that we can access any key of the object through an index of type string, which in turn returns another string.

In any case, other errors occur on the first try, such as:

error TS2411: Property 'description' of type 'string | undefined' is not assignable to string index type 'string'.
error TS2411: Property 'id' of type 'number | undefined' is not assignable to string index type 'string'.
Copy the code

This is because some attributes on the interface are optional and may be undefined, and the type is not always a string (for example, id is a number).

We can try to solve this problem with associative types, which is a TypeScript syntax that defines associative types between two or more other types:

interfaceLink { description? :string; id? :number;
  url: string;
  [index: string] :string | number | undefined;
}
Copy the code

The following line:

[index: string] :string | number | undefined;
Copy the code

Indicates that index is a string that may return another string, a number, or an undefined value. Trying to compile again, there is another error:

error TS2339: Property 'match' does not exist on type 'string | number'.
return arrayElement[lookupKey].match(regex);
Copy the code

It makes sense. The match method only works on strings, and our index may return a number. To fix this error, we can use Anyas as a solution:

interfaceLink { description? :string; id? :number;
  url: string;
  [index: string] :any;
}
Copy the code

The TypeScript compiler is happy again, but at what cost?

Now it’s time to turn our attention to another fundamental feature of TypeScript: the return types of functions.

TypeScript tutorial: Return types of functions

There’s a lot of new stuff so far. In short, I’ve skipped over another useful feature of TypeScript: the return types of functions.

To understand why it is convenient to add type annotations to return values, imagine that I am playing with your fancy function. This is the original version:

function filterByTerm(
  input: Array<Link>,
  searchTerm: string,
  lookupKey: string = "url"
) {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement[lookupKey].match(regex);
  });
}
Copy the code

If called as-is, passing in the array of links you saw earlier and the search term “string3”, it should return an array of objects, as expected:

filterByTerm(arrOfLinks, "string3"); 

// EXPECTED OUTPUT:
// [ { url: 'string3' } ]
Copy the code

But now consider another variation:

function filterByTerm(
  input: Array<Link>,
  searchTerm: string,
  lookupKey: string = "url"
) {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input
    .filter(function(arrayElement) {
      return arrayElement[lookupKey].match(regex);
    })
    .toString();
}
Copy the code

If called now, using the same Link array and the search term “string3”, it will return “[object object]”! :

filterByTerm(arrOfLinks, "string3");

// WRONG OUTPUT:
// [object Object]
Copy the code

Can you spot the problem? Tip: toString.

The function doesn’t work as expected, and you’ll never know until you get to production (or test your code). Fortunately, TypeScript catches these errors just as you would in an editor.

Here’s the fix (just the relevant part):

function filterByTerm(/* omitted for brevity */) :Array<Link> {
 /* omitted for brevity */
}
Copy the code

How does it work? By adding type annotations to the function body, we tell TypeScript to expect another array as the return value. Now the bug is easy to spot. Here is the code so far (modified version):

interfaceLink { description? :string; id? :number;
  url: string;
  [index: string] :any;
}

function filterByTerm(
  input: Array<Link>,
  searchTerm: string,
  lookupKey: string = "url"
) :Array<Link> {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input
    .filter(function(arrayElement) {
      return arrayElement[lookupKey].match(regex);
    })
    .toString();
}

const obj1: Link = { url: "string1" };
const obj2: Link = { url: "string2" };
const obj3: Link = { url: "string3" };

const arrOfLinks: Array<Link> = [obj1, obj2, obj3];

filterByTerm(arrOfLinks, "string3");
Copy the code

Compile the code

npm run tsc
Copy the code

And check for errors:

error TS2322: Type 'string' is not assignable to type 'Link[]'.
Copy the code

That’s great. We expect linked arrays, not strings. To fix the error, remove.tostring () from the end of the filter and compile the code again. It should be ok now!

We added another layer of protection to the code. Of course, this bug can be found through unit testing. TypeScript is a good security layer, not a complete replacement for testing.

Let’s continue exploring type aliases!

TypeScript Aliases vs Interfaces

So far, we’ve seen interfaces as a tool for describing objects and custom types. But in other people’s code, you might also notice keyword types.

Obviously, interface and type are used interchangeably in TypeScript, but they are different in many ways. This is confusing for TypeScript beginners.

Remember: An interface in TypeScript is the shape of something, most of the time a complex object.

On the other hand, a type can also be used to describe a custom shape, but it’s just an alias, or in other words, a label for a custom type. For example, let’s imagine an interface with several fields, one of which is a combination of Boolean, number, and string:

interface Example {
  authenticated: boolean | number | string;
  name: string;
}
Copy the code

For example, with a type alias, you can extract a custom union type and create a tag called Authenticated:

type Authenticated = boolean | number | string;

interface Example {
  authenticated: Authenticated;
  name: string;
}
Copy the code

This way, you can isolate changes so that you don’t have to copy/paste union types throughout your code base.

If you want to apply Type to our example (filterByTerm), create a new label called Links and assign Array to it. This way you can reference types:

// the new label
type Links = Array<Link>;
// the new label

function filterByTerm(
  input: Links,
  searchTerm: string,
  lookupKey: string = "url"
) :Links {
  if(! searchTerm)throw Error("searchTerm cannot be empty");
  if(! input.length)throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement[lookupKey].match(regex);
  });
}

const obj1: Link = { url: "string1" };
const obj2: Link = { url: "string2" };
const obj3: Link = { url: "string3" };

const arrOfLinks: Links = [obj1, obj2, obj3];

filterByTerm(arrOfLinks, "string3");
Copy the code

Now, this is not the smartest example of tag types but you get the point. So what should you use between an interface and a type? I prefer interfaces for complex objects. The TypeScript documentation also suggests an approach:

Because the ideal properties of software are open to extension, you should always use interfaces on type aliases if possible.

I hope this helps clear up any confusion.

In the next section, we’ll take a quick look at two other TypeScript topics before saying goodbye. Keep going!

TypeScript Tutorial for beginners: More about interfaces and objects

Functions are the first class of citizens of JavaScript, and objects are the most important entities in the language.

Objects are mostly containers for key/value pairs, so it’s not surprising that they can also hold functions. When a function resides inside an object, it can access the “host” object with the keyword this:

const tom = {
  name: "Tom".city: "Munich".age: 33.printDetails: function() {
    console.log(`The ${this.name} - The ${this.city}`); }};Copy the code

So far, you’ve seen TypeScript interfaces applied to simple objects that describe strings and numbers. But they can do more. Let’s take an example. Create a new file called interfaces-functions. Ts, code as follows:

const tom = {
  name: "Tom".city: "Munich".age: 33.printDetails: function() {
    console.log(`The ${this.name} - The ${this.city}`); }};Copy the code

It’s a JavaScript object, but it needs a type. Let’s make an interface to make Tom conform to a well-defined shape. How about “IPerson”? In the meantime, let’s also apply the new interface to Tom:

interface IPerson {
  name: string;
  city: string;
  age: number;
}

const tom: IPerson = {
  name: "Tom".city: "Munich".age: 33.printDetails: function() {
    console.log(`The ${this.name} - The ${this.city}`); }};Copy the code

A compiler error

interfaces-functions.ts:11:3 - error TS2322: Type '{ name: string; city: string; age: number; printDetails: () => void; } ' is not assignable to type 'IPerson'.
  Object literal may only specify known properties, and 'printDetails' does not exist in type 'IPerson'.
Copy the code

It’s cool that IPerson doesn’t have any property called printDetails, but more importantly it should be a function. Fortunately, TypeScript interfaces can also describe functions. The method is as follows:

interface IPerson {
  name: string;
  city: string;
  age: number;
  printDetails(): void;
}
Copy the code

Here we add the property printDetails of a type function that returns void. Void is useful as a return value of a function… Don’t return anything.

Functions that output to the console actually return nothing. If you want to return a string from printDetails, you can change the return type to String:

interface IPerson {
  name: string;
  city: string;
  age: number;
  printDetails(): string;
}

const tom: IPerson = {
  name: "Tom".city: "Munich".age: 33.printDetails: function() {
    return `The ${this.name} - The ${this.city}`; }};Copy the code

Now, what if the function takes arguments? In the interface, you can add type comments to them:

interface IPerson {
  name: string;
  city: string;
  age: number;
  printDetails(): string;
  anotherFunc(a: number.b: number) :number;
}
Copy the code

If you start typing “an…” into objects that implement IPerson Most ides will do this automatically for you. Best developer productivity.