Saturday, May 14, 2022
HomeWebsite DesignA Step-by-Step TypeScript Tutorial for Learners

A Step-by-Step TypeScript Tutorial for Learners


You’ve most likely heard of TypeScript — the language created and maintained by Microsoft that’s had a huge effect on the Net, with many distinguished tasks embracing and migrating their code to TypeScript. TypeScript is a typed superset of JavaScript. In different phrases, it provides varieties to JavaScript — and therefore the title. However why would you need these varieties? What advantages do they carry? And do you have to rewrite your complete codebase to make the most of them? These questions, and extra, will likely be answered on this TypeScript tutorial for rookies.

We assume a primary data of JavaScript and its tooling, however zero prior data of TypeScript is required to comply with alongside.

Some Inaccurate JavaScript Code

To begin with, let’s take a look at some pretty customary plain JavaScript code that you just may come throughout in any given codebase. It fetches some photos from the Pexels API and inserts them to the DOM.

Nevertheless, this code has a number of typos in it which are going to trigger issues. See in the event you can spot them:

const PEXELS_API_KEY = '...';

async perform fetchImages(searchTerm, perPage) {
  const consequence = await fetch(`https://api.pexels.com/v1/search?question=${searchTerm}&per_page=${perPage}`, {
    headers: {
      Authorization: PEXELS_API_KEY,
    }
  });
  const knowledge = await consequence.json();

  const imagesContainer = doc.qerySelector('#images-container');
  for (const picture of knowledge.images) {
    const img = doc.createElement('picture');
    img.src = picture.src.medium;
    imagesContainer.append(img);
  }
}

fetchImages('canines', 5);
fetchImages(5, 'cats');
fetchImages('puppies');

Can you notice the problems within the above instance? After all, in the event you ran this code in a browser you’d instantly get errors, however by benefiting from TypeScript we will get the errors faster by having TypeScript spot these points in our editor.

Shortening this suggestions loop is efficacious — and it will get extra beneficial as the scale of your challenge grows. It’s straightforward to identify errors in these 30 traces of code, however what in the event you’re working in a codebase with hundreds of traces? Would you notice any potential points simply then?

Be aware: there’s no must get hold of an API key from Pexels to comply with together with this TypeScript tutorial. Nevertheless, in the event you’d prefer to run the code, an API secret’s completely free: you simply must join an account after which generate one.

Operating TypeScript from the Editor

As soon as upon a time, TypeScript required that each one recordsdata be written as .ts recordsdata. However lately, the onboarding ramp is smoother. You don’t want a TypeScript file to jot down TypeScript code: as a substitute, we will run TypeScript on any JavaScript file we fancy!

In case you’re a VS Code consumer (don’t panic in the event you aren’t — we’ll get to you!), it will work out the field with no further necessities. We will allow TypeScript’s checking by including this to the very prime of our JavaScript file (it’s necessary that it’s the primary line):


It’s best to then get some squiggly purple errors in your editor that spotlight our errors, as pictured under.

TypeScript showing errors in VS Code

You also needs to see a cross within the backside left-hand nook with a two by it. Clicking on it will reveal the issues which were noticed.

Errors displayed in the VS Code console

And simply since you’re not on VS Code doesn’t imply you’ll be able to’t get the identical expertise with TypeScript highlighting errors. Most editors lately assist the Language Server Protocol (generally known as LSP), which is what VS Code makes use of to energy its TypeScript integration.

It’s effectively value looking on-line to seek out your editor and the really helpful plugins to have it arrange.

Putting in and Operating TypeScript Domestically

In case you’re not on VS Code, otherwise you’d like a normal answer, you can too run TypeScript on the command line. On this part, I’ll present you ways.

First, let’s generate a brand new challenge. This step assumes you have got Node and npm put in upon your machine:

mkdir typescript-demo
cd typescript demo
npm init -y

Subsequent, add TypeScript to your challenge:

npm set up --save-dev typescript

Be aware: you might set up TypeScript globally in your machine, however I like to put in it per-project. That manner, I guarantee I’ve management over precisely which model of TypeScript every challenge makes use of. That is helpful if in case you have a challenge you’ve not touched for some time; you’ll be able to maintain utilizing an older TS model on that challenge, while having a more moderen challenge utilizing a more moderen model.

As soon as it’s put in, you’ll be able to run the TypeScript compiler (tsc) to get the identical errors (don’t fear about these further flags, as we’ll speak extra about them shortly):

npx tsc index.js --allowJs --noEmit --target es2015
index.js:13:36 - error TS2551: Property 'qerySelector' doesn't exist on sort 'Doc'. Did you imply 'querySelector'?

13   const imagesContainer = doc.qerySelector('#images-container');
                                      ~~~~~~~~~~~~

  node_modules/typescript/lib/lib.dom.d.ts:11261:5
    11261     querySelector<Okay extends keyof HTMLElementTagNameMap>(selectors: Okay): HTMLElementTagNameMap[K] | null;
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    'querySelector' is said right here.

index.js:16:9 - error TS2339: Property 'src' doesn't exist on sort 'HTMLElement'.

16     img.src = picture.src.medium;
           ~~~

Discovered 2 errors.

You possibly can see that TypeScript on the command line highlights the identical JavaScript code errors that VS Code highlighted within the screenshot above.

Fixing the Errors in Our JavaScript Code

Now that we’ve got TypeScript up and working, let’s take a look at how we will perceive after which rectify the errors that TypeScript is flagging.

Let’s check out our first error.

Property qerySelector doesn’t exist on sort Doc

index.js:13:36 - error TS2551: Property 'qerySelector' doesn't exist on sort 'Doc'. Did you imply 'querySelector'?

13   const imagesContainer = doc.qerySelector('#images-container');

  node_modules/typescript/lib/lib.dom.d.ts:11261:5
    11261     querySelector<Okay extends keyof HTMLElementTagNameMap>(selectors: Okay): HTMLElementTagNameMap[K] | null;
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    'querySelector' is said right here.

This may look fairly overwhelming in the event you’re not used to studying TypeScript errors, so don’t panic if it appears a bit odd! TypeScript has noticed that, on line 13, we’ve referred to as a technique doc.qerySelector. We meant doc.querySelector however made a mistake when typing. We’d have discovered this out once we tried to run our code within the browser, however TypeScript is ready to make us conscious of it sooner.

The subsequent half the place it highlights lib.dom.d.ts and the querySelector<Okay...> perform is diving into extra superior TypeScript code, so don’t fear about that but, however at a excessive stage it’s TypeScript displaying us that it understands that there’s a technique referred to as querySelector, and it suspects we would have wished that.

Let’s now zoom in on the final a part of the error message above:

index.js:13:36 - error TS2551: Property 'qerySelector' doesn't exist on sort 'Doc'. Did you imply 'querySelector'?

Particularly, I wish to take a look at the textual content didn't exist on sort 'Doc'. In TypeScript (and broadly in each typed language), objects have what’s referred to as a sort.

In TypeScript, numbers like 1 or 2.5 have the kind quantity, strings like "good day world" have the kind string, and an occasion of an HTML Factor has the kind HTMLElement. That is what permits TypeScript’s compiler to test that our code is sound. As soon as it is aware of the kind of one thing, it is aware of what capabilities you’ll be able to name that take that one thing, or what strategies exist on it.

Be aware: in the event you’d prefer to study extra about knowledge varieties, please seek the advice of “Introduction to Information Sorts: Static, Dynamic, Robust & Weak”.

In our code, TypeScript has seen that we’ve referred to doc. This can be a world variable within the browser, and TypeScript is aware of that and is aware of that it has the kind of Doc. This sort paperwork (in the event you pardon the pun!) the entire strategies we will name. For this reason TypeScript is aware of that querySelector is a technique, and that the misspelled qerySelector shouldn’t be.

We’ll see extra of those varieties as we undergo additional TypeScript tutorials, however that is the place all of TypeScript’s energy comes from. Quickly we’ll outline our personal varieties, that means actually we will prolong the kind system to have data about all of our code and what we will and might’t do with any explicit object in our codebase.

Now let’s flip our consideration to our subsequent error, which is barely much less clear.

Property src doesn’t exist on sort HTMLElement

index.js:16:9 - error TS2339: Property 'src' doesn't exist on sort 'HTMLElement'.

16     img.src = picture.src.medium;

That is a type of errors the place typically you need to look barely above the error to seek out the issue. We all know that an HTML picture factor does have a src attribute, so why doesn’t TypeScript?

const img = doc.createElement('picture');
img.src = picture.src.medium;

The error right here is on the primary line: once you create a brand new picture factor, you need to name doc.createElement('img') (as a result of the HTML tag is <img>, not <picture>). As soon as we do this, the error goes away, as a result of TypeScript is aware of that, once you name doc.createElement('img'), you get again a component that has a src property. And that is all right down to the varieties.

Whenever you name doc.createElement('div'), the thing returned is of the kind HTMLDivElement. Whenever you name doc.createElement('img'), the thing returned is of sort HTMLImageElement. HTMLImageElement has a src property declared on it, so TypeScript is aware of you’ll be able to name img.src. However HTMLDivElement doesn’t, so TypeScript will error.

Within the case of doc.createElement('picture'), as a result of TypeScript doesn’t learn about any HTML factor with the tag picture, it should return an object of sort HTMLElement (a generic HTML factor, not particular to 1 tag), which additionally lacks the src property.

As soon as we repair these two errors and re-run TypeScript, you’ll see we get again nothing, which reveals that there have been no errors. In case you’ve configured your editor to point out errors, hopefully there are actually none displaying.

Learn how to Configure TypeScript

It’s a little bit of a ache to have so as to add // @ts-check to every file, and once we run the command within the terminal having so as to add these further flags. TypeScript enables you to as a substitute allow it on a JavaScript challenge by making a jsconfig.json file.

Create jsconfig.json within the root listing of our challenge and place this inside it:

{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "goal": "es2015"
  },
  "embody": ["*.js"]
}

This configures the TypeScript compiler (and your editor’s TS integration) to:

  1. Test JavaScript recordsdata (the checkJs possibility).
  2. Assume we’re constructing in an ES2015 surroundings (the goal possibility). Defaulting to ES2015 means we will use issues like guarantees with out TypeScript giving us errors.
  3. Not output any compiled recordsdata (the noEmit possibility). Whenever you’re writing TypeScript code in TypeScript supply recordsdata, you want the compiler to generate JavaScript code so that you can run within the browser. As we’re writing JavaScript code that’s working within the browser, we don’t want the compiler to generate any recordsdata for us.
  4. Lastly, embody: ["*.js"] instructs TypeScript to take a look at any JavaScript file within the root listing.

Now that we’ve got this file, you’ll be able to replace your command-line instruction to this:

npx tsc -p jsconfig.json

This may run the compiler with our configuration file (the -p right here is brief for “challenge”), so that you now not must go all these flags by when working TypeScript.

Working in strict mode

Now we’re right here, let’s see how we will make TypeScript much more thorough when checking our code. TypeScript helps one thing referred to as “strict mode”, which instructs TypeScript to test our code extra completely and be certain that we take care of any potential occasions the place, for instance, an object is perhaps undefined. To make this clearer, let’s flip it on and see what errors we get. Add "strict": true to the "compilerOptions" a part of jsconfig.json, after which re-run TypeScript on the command line.

Whenever you make a change to the jsconfig.json file, you might discover you have to restart your editor for it to choose up these modifications. So in the event you’re not seeing the identical errors as me, give {that a} go.

npx tsc -p jsconfig.json
index.js:3:28 - error TS7006: Parameter 'searchTerm' implicitly has an 'any' sort.

3 async perform fetchImages(searchTerm, perPage) {
                             ~~~~~~~~~~

index.js:3:40 - error TS7006: Parameter 'perPage' implicitly has an 'any' sort.

3 async perform fetchImages(searchTerm, perPage) {
                                         ~~~~~~~

index.js:15:5 - error TS2531: Object is presumably 'null'.

15     imagesContainer.append(img);
       ~~~~~~~~~~~~~~~

Discovered 3 errors.

Let’s begin with the final error first and are available again to the others:

index.js:15:5 - error TS2531: Object is presumably 'null'.

15     imagesContainer.append(img);
       ~~~~~~~~~~~~~~~

And let’s take a look at how imagesContainer is outlined:

const imagesContainer = doc.querySelector('#images-container');

Turning on strict mode has made TypeScript stricter at making certain that values we count on to exist do exist. On this case, it’s not assured that doc.querySelector('#images-container') will really return a component; what if it’s not discovered? doc.querySelector will return null if a component shouldn’t be discovered, and now we’ve enabled strict mode, TypeScript is telling us that imagesContainer may really be null.

Union Sorts

Previous to turning on strict mode, the kind of imagesContainer was Factor, however now we’ve turned on strict mode the kind of imagesContainer is Factor | null. The | (pipe) operator creates union varieties — which you’ll be able to learn as “or” — so right here imagesContainer is of sort Factor or null. When TypeScript says to us Object is presumably 'null', that’s precisely what it’s telling us, and it needs us to make sure that the thing does exist earlier than we use it.

Let’s repair this by throwing an error ought to we not discover the photographs container factor:

const imagesContainer = doc.querySelector('#images-container');
if (imagesContainer === null) {
  throw new Error('Couldn't discover images-container factor.')
}

for (const picture of knowledge.images) {
  const img = doc.createElement('img');
  img.src = picture.src.medium;
  imagesContainer.append(img);
}

TypeScript is now completely satisfied; we’ve handled the null case by throwing an error. TypeScript is sensible sufficient to grasp now that, ought to our code not throw an error on the third line within the above snippet, imagesContainer shouldn’t be null, and due to this fact should exist and should be of sort Factor.

Its sort was Factor | null, but when it was null we’d have thrown an error, so now it should be Factor. This performance is called sort narrowing and is a really helpful idea to pay attention to.

Implicit any

Now let’s flip our consideration to the remaining two errors we’ve got:

index.js:3:28 - error TS7006: Parameter 'searchTerm' implicitly has an 'any' sort.

3 async perform fetchImages(searchTerm, perPage) {
                             ~~~~~~~~~~

index.js:3:40 - error TS7006: Parameter 'perPage' implicitly has an 'any' sort.

3 async perform fetchImages(searchTerm, perPage) {

One of many implications of turning on strict mode is that it activates a rule referred to as noImplicitAny. By default, when TypeScript doesn’t know the kind of one thing, it should default to giving it a particular TypeScript sort referred to as any. any shouldn’t be a terrific sort to have in your code, as a result of there are not any guidelines related to it by way of what the compiler will test. It is going to enable something to occur.

I prefer to image it because the compiler throwing its arms up within the air and saying “I can’t make it easier to right here!” Utilizing any disables any helpful sort checking for that specific variable, so I extremely suggest avoiding it.

Describe the Perform Signature with JSDoc

The 2 errors above are TypeScript telling us that we’ve not instructed it what varieties the 2 variables our perform takes are, and that it’s defaulting them again to any. The excellent news is that giving TypeScript this info used to imply rewriting your file into TypeScript code, however TypeScript now helps a hefty subset of JSDoc syntax, which helps you to present sort info to TypeScript by way of JavaScript feedback.

For instance, right here’s how we will present sort info to our fetchImages perform:


async perform fetchImages(searchTerm, perPage) {
  
}

All JSDoc feedback should begin with /** (be aware the additional * originally) and inside them we use particular tags, beginning with @, to indicate sort properties. Right here we declare two parameters (@param), after which we put their sort in curly braces (similar to common JavaScript objects).

Right here we make it clear that searchTerm is a string and perPage is a quantity. Whereas we’re at it, we additionally use @return to declare what this perform returns. In our case it returns nothing, and the kind we use in TypeScript to declare that’s void.

Let’s now re-run the compiler and see what it says:

npx tsc -p jsconfig.json
index.js:30:13 - error TS2345: Argument of sort 'quantity' shouldn't be assignable to parameter of sort 'string'.

30 fetchImages(5, 'cats')
               ~

index.js:31:1 - error TS2554: Anticipated 2 arguments, however acquired 1.

31 fetchImages('puppies')
   ~~~~~~~~~~~~~~~~~~~~~~

  index.js:9:40
    9 async perform fetchImages(searchTerm, perPage) {
                                             ~~~~~~~
    An argument for 'perPage' was not supplied.

Discovered 2 errors.

That is the fantastic thing about TypeScript. Giving the compiler further info, it may possibly now spot errors in how we’re calling the code that it couldn’t earlier than. On this case, it’s discovered two calls to fetchImages the place we’ve acquired the arguments within the incorrect order, and the second the place we’ve forgotten the perPage argument (neither searchTerm, perPage are non-obligatory parameters).

Let’s simply delete these calls, however I hope it helps reveal the facility of the compiler and the advantages of giving the compiler further sort info.

Declaring Information Sorts Utilizing an Interface

Though not flagged by the compiler, one subject our code nonetheless has is on this line:

const knowledge = await consequence.json();

The issue right here is that the return sort of await consequence.json() is any. It is because, once you take an API response and convert it into JSON, TypeScript has no concept what knowledge is in there, so it defaults to any. However as a result of we all know what the Pexels API returns, we can provide it some sort info by utilizing TypeScript interfaces. These allow us to inform TypeScript in regards to the form of an object: what properties it has, and what values these properties have.

Let’s declare an interface — once more, utilizing JSDoc syntax, that represents the info returned from the Pexels API. I used the Pexels API reference to determine what knowledge is returned. On this case, we’ll really outline two interfaces: one will declare the form of a single picture that the Pexels API returns, and the opposite will declare the general form of the response from the API.

To outline these interfaces utilizing JSDoc, we use @typedef, which lets us declare extra complicated varieties. We then use @property to declare single properties on that interface. For instance, right here’s the kind I create for a person Picture. Sorts ought to all the time begin with a capital letter.

In case you’d prefer to see a full reference to all supported JSDoc performance, the TypeScript web site has an intensive checklist full with examples.


This sort says that any object typed as a Picture could have one property, src, which itself is an object with three string properties: medium, massive and thumbnail. You’ll discover that the Pexels API returns extra; you don’t need to declare each property an object has in the event you don’t wish to, however simply the subset you want. Right here, our app at present solely makes use of the medium picture, however I’ve declared a few further sizes we would need sooner or later.

Now that we’ve got that sort, we will declare the kind PexelsSearchResponse, which can signify what we get again from the API:


That is the place you’ll be able to see the worth of declaring your personal varieties; we declare that this object has one property, images, after which declare that its worth is an array, the place every merchandise is of sort Picture. That’s what the Array<X> syntax denotes: it’s an array the place every merchandise within the array is of sort X. [1, 2, 3] can be an Array<quantity>, for instance.

As soon as we’ve carried out that, we will then use the @sort JSDoc remark to inform TypeScript that the info we get again from consequence.json() is of the kind PexelsSearchResponse:


const knowledge = await consequence.json();

@sort isn’t one thing it is best to attain for on a regular basis. Usually, you need the compiler to intelligently determine the kind of issues, relatively than need to bluntly inform it. However as a result of consequence.json() returns any, we’re good right here to override that with our sort.

Check if every thing is working

To show that that is working, I’ve intentionally misspelled medium when referencing the picture’s URL:

for (const picture of knowledge.images) {
  const img = doc.createElement('img');
  img.src = picture.src.mediun; 
  imagesContainer.append(img);
}

If we run TypeScript once more, we’ll see the difficulty that TypeScript wouldn’t have noticed if we hadn’t carried out the work we simply did to declare the interface:

index.js:35:25 - error TS2551: Property 'mediun' doesn't exist on sort '{ medium: string; massive: string; thumbnail: string; }'. Did you imply 'medium'?

35     img.src = picture.src.mediun;
                           ~~~~~~

  index.js:18:18
    18    * @property {{medium: string, massive: string, thumbnail: string}} src
                        ~~~~~~
    'medium' is said right here.

Discovered 1 error.

Conclusion

TypeScript has so much to supply builders engaged on difficult codebases. Its means to shorten the suggestions loop and present you errors earlier than you need to recompile and cargo up the browser is admittedly beneficial. We’ve seen how it may be used on any present JavaScript challenge (avoiding the necessity to rewrite your code into .ts recordsdata) and the way straightforward it’s to get began.

I hope you’ve loved this TypeScript tutorial for rookies. In remainder of this three-part tutorial sequence, which is obtainable over on SitePoint Premium, we’ll begin placing this data into motion and present how one can construct a real-world software from scratch utilizing TypeScript that takes full benefit of the TypeScript ecosystem. This may cowl issues like dynamically rendering markup and consuming a third-party API, permitting customers to seek for images or movies and star them as a favourite, and storing these favorites in native storage.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments