Just crossed 5k follower on dev.to! Thank you everyone! What a fantastic community! Who's on Twitter too? Let's connect => I'm here.
What is TypeScript and why you may want to use it? Learn more with this TypeScript tutorial for beginners and start adding types to your JavaScript code!
Originally published on valentinog.com/blog
In this episode:
- TypeScript types
- TypeScript interfaces
- typing variables
Dipping our toes into TypeScript types
TypeScript revolves around types and looks like our code has no types at all. Time to add some. We're going to fix function parameters first. By looking at how the function is called it seems it takes strings as arguments:
filterByTerm("input string", "java");
Are we sure? Let's add your first type annotation to the function. Here's how:
function filterByTerm(input: string, searchTerm: string) {
// omitted
}
// omitted
That's it! By adding types to the parameters we're migrating our code from pure JavaScript to TypeScript. But if you try to compile the code:
npm run tsc
here's what happens:
filterByTerm.ts:5:16 - error TS2339: Property 'filter' does not exist on type 'string'.
Can you see how TypeScript is guiding you? The problem is with the filter function:
function filterByTerm(input: string, searchTerm: string) {
// omitted
return input.filter(function(arrayElement) {
return arrayElement.url.match(regex);
});
}
We're telling TypeScript that "input" is a string but later in the code we call the filter method on it, which belongs to arrays. What we really want instead is marking "input" as an array of something, maybe an array of strings?
For doing so you have two options. Option 1 with string[]:
function filterByTerm(input: string[], searchTerm: string) {
// omitted
}
or if you like this syntax, option 2 with Array:
function filterByTerm(input: Array<string>, searchTerm: string) {
// omitted
}
Personally I like option 2 more. Now let's try to compile again (npm run tsc) and 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");
TypeScript doesn't want to leave us alone I suppose. Don't blame it, we marked input as an array of strings and now we're trying to pass in a string. That's an easy fix! Let's pass an array of strings instead:
filterByTerm(["string1", "string2", "string3"], "java");
And here's the complete 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");
Looks good to me. But if you compile it's not (npm run tsc):
filterByTerm.ts:6:25 - error TS2339: Property 'url' does not exist on type 'string'.
Ok TypeScript, fair enough. We're passing in an array of strings but later in the code we try to access a property named "url":
return arrayElement.url.match(regex);
That means we want an array of objects, not an array of strings. Let's fix that into the next section!
TypeScript tutorial for beginners: TypeScript objects and interfaces
We left with TypeScript complaining (what a surprise) because filterByTerm has been passed an array of strings. "url" property does not exists on type string TypeScript yelled. Let's help TypeScript then by passing an array of objects, where every object has the required url property:
filterByTerm(
[{ url: "string1" }, { url: "string2" }, { url: "string3" }],
"java"
);
and while you're there update the function signature so that it takes an array of objects:
function filterByTerm(input: Array<object>, searchTerm: string) {
// omitted
}
Now let's compile the code:
npm run tsc
and admire the output:
filterByTerm.ts:6:25 - error TS2339: Property 'url' does not exist on type 'object'.
Here we go again! It makes sense, at least in TypeScript: the generic JavaScript object does not have any property named "url". And for me this is where TypeScript really starts to shine.
So what's really the difference between JavaScript and TypeScript? It's not that JavaScript does not have types. JavaScript has types but they are "loose", dynamic. In other words you can change a variable's type later in the code, or assign new properties to (almost) any object.
Now, at first it will look like alien syntax, but once you get accustomed to interfaces you'll start to use them all over the place. But what's an interface by the way? An interface in TypeScript is like a contract. Or put it another way an interface is like a "model" for your entity.
By taking a look at our code we can think of a simple "model" named Link for an object whose shape should conform to the following pattern:
- it must have a url property of type string
In TypeScript you would define that "model" with an interface, like so (put the following code at the top of filterByTerm.ts:
interface ILink {
url: string;
}
With the interface declaration we say "I want to use that shape in my TypeScript code from now on". That's not valid JavaScript syntax of course and it will be stripped away during compilation.
TIP: it is a good idea to prefix interfaces with a capital I, that's a convention in TypeScript
Now we can use our interface ILink, which is actually also a custom TypeScript type, by fixing the parameter "input":
function filterByTerm(input: Array<ILink>, searchTerm: string) {
// omitted
}
With this fix we say to TypeScript "expect an array of ILink" as the input for that function. Here's the complete code:
interface ILink {
url: string;
}
function filterByTerm(input: Array<ILink>, 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"
);
At this point all the errors should go away and you can run:
npm run tsc
The compilation step will produce a file named filterByTerm.js with plain JavaScript code in the project folder. You can check out the file and see how TypeScript specific declaration are stripped away.
Since "alwaysStrict" is set true the TypeScript compiler also emits "use strict" at the top of filterByTerm.js.
Great job on your first TypeScript code! In the next section we'll explore interfaces a bit more.
TypeScript tutorial for beginners: interfaces and fields
TypeScript interfaces are one of the most powerful construct of the language. Interfaces help in shaping "models" across your application so that any developer can pick that shape and conform to it when writing code.
So far we defined a simple interface, ILink:
interface ILink {
url: string;
}
If you want to add more fields to the interface it's a matter of declaring them inside the block:
interface ILink {
description: string;
id: number;
url: string;
}
Now any object of type ILink must "implement" the new fields, otherwise you get an error. In fact by compiling the code with:
npm run tsc
TypeScript screams at you:
filterByTerm.ts:17:4 - error TS2739: Type '{ url: string; }' is missing the following properties from type 'ILink': description, id
The problem is with the argument of our function:
filterByTerm(
[{ url: "string1" }, { url: "string2" }, { url: "string3" }],
"java"
);
TypeScript is able to deduct by looking at the function declaration that the argument is of type Array of ILink. Thus any object inside that array must have (implement) all the fields defined in the interface ILink.
Most of the time that's far from optimal. After all we don't know if every new object of type ILink will ever have all the fields. Worry not, to make the compilation pass we can declare interface's fields optional with a question mark:
interface ILink {
description?: string;
id?: number;
url: string;
}
Now both the editor and the compiler will be fine. Yet TypeScript interfaces can do a lot more, in the next sections we'll see how to extend them. But first a brief note about variables in TypeScript.
TypeScript tutorial for beginners: typing variables
So far you've seen how to add types to function's parameters:
function filterByTerm(input: Array<ILink>, searchTerm: string) {
//
}
TypeScript is not limited to that, of course you can also add types to any variable. Let's extract function's arguments one by one, for the sake of illustrating the example. First I'm going to extract every single object:
const obj1: ILink = { url: "string1" };
const obj2: ILink = { url: "string2" };
const obj3: ILink = { url: "string3" };
Notice how I can say to TypeScript that obj1, obj2 and obj3 are of type ILink. In "vanilla" JavaScript you would write:
const obj1 = { url: "string1" };
const obj2 = { url: "string2" };
const obj3 = { url: "string3" };
Next up we can define an array of ILink like so:
const arrOfLinks: Array<ILink> = [obj1, obj2, obj3];
And finally the search term:
const term: string = "java";
Here's the complete code:
interface ILink {
description?: string;
id?: number;
url: string;
}
function filterByTerm(input: Array<ILink>, 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: ILink = { url: "string1" };
const obj2: ILink = { url: "string2" };
const obj3: ILink = { url: "string3" };
const arrOfLinks: Array<ILink> = [obj1, obj2, obj3];
const term: string = "java";
filterByTerm(arrOfLinks, term);
Ok, I feel you. TypeScript looks more verbose and sometimes redundant compared to JavaScript. But with time you'll see that the more you add types, the more your code becomes robust.
The more you help TypeScript to understand the intent of your code by adding type annotations, the more you'll be fine later. And your developer experience will skyrocket.
For example now that arrOfLinks is associate with the correct type (array of ILink), your editor is able to infer that every object in the array has a property named url, as defined in the interface ILink:
Now tell me this isn't fantastic because indeed it is. TypeScript has a lot more types besides string, Array and number.
There are booleans, tuples, "any", never, enums. With time you'll learn them all. If you're curious check out the documentation for the basic types.
Now let's move on to extending interfaces.
Stay tuned for part 3!
Top comments (0)