banner



Can I Learn Typescript Without Javascript

Learn TypeScript – The Ultimate Beginners Guide

TypeScript has become increasingly popular over the last few years, and many jobs are now requiring developers to know TypeScript.

But don't be alarmed – if you already know JavaScript, you volition exist able to selection up TypeScript quickly.

Even if you don't plan on using TypeScript, learning information technology will give you a improve understanding of JavaScript – and make you a better developer.

In this article, you lot will learn:

  • What is TypeScript and why should I learn it?
  • How to gear up upwardly a project with TypeScript
  • All of the main TypeScript concepts (types, interfaces, generics, blazon-casting, and more than...)
  • How to utilize TypeScript with React

I also made a TypeScript cheat canvass PDF and poster that summarizes this article down to one page. This makes it piece of cake to look up and revise concepts/syntax quickly.

TypeScript cheat sheet PDF
TypeScript crook sheet PDF

What is TypeScript?

TypeScript is a superset of JavaScript, meaning that it does everything that JavaScript does, but with some added features.

The main reason for using TypeScript is to add static typing to JavaScript. Static typing means that the blazon of a variable cannot be changed at any point in a programme. It tin can prevent a LOT of bugs!

On the other paw, JavaScript is a dynamically typed linguistic communication, meaning variables tin change blazon. Hither's an example:

                // JavaScript let foo = "hello"; foo = 55; // foo has inverse type from a string to a number - no trouble  // TypeScript let foo = "hello"; foo = 55; // ERROR - foo cannot modify from string to number                              

TypeScript cannot be understood by browsers, then it has to be compiled into JavaScript by the TypeScript Compiler (TSC) – which we'll discuss soon.

Is TypeScript worth it?

Why you should use TypeScript

  • Research has shown that TypeScript tin spot 15% of common bugs.
  • Readability – information technology is easier to run into what the code it supposed to do. And when working in a team, information technology is easier to meet what the other developers intended to.
  • Information technology'south popular – knowing TypeScript volition enable you lot to utilise to more than good jobs.
  • Learning TypeScript volition give you a better agreement, and a new perspective, on JavaScript.

Here's a brusk article I wrote demonstrating how TypeScript can forbid irritating bugs.

Drawbacks of TypeScript

  • TypeScript takes longer to write than JavaScript, equally yous have to specify types, so for smaller solo projects it might non be worth using it.
  • TypeScript has to be compiled – which can take time, especially in larger projects.

But the extra time that yous have to spend writing more precise code and compiling will be more than than saved by how many fewer bugs yous'll accept in your code.

For many projects – especially medium to large projects – TypeScript will save you lots of time and headaches.

And if you already know JavaScript, TypeScript won't be too difficult to learn. Information technology's a bang-up tool to have in your arsenal.

How to Set up a TypeScript Project

Install Node and the TypeScript Compiler

Get-go, ensure y'all have Node installed globally on your machine.

Then install the TypeScript compiler globally on your motorcar by running the post-obit control:

                npm i -thousand typescript                              

To check if the installation is successful (it will return the version number if successful):

                tsc -v                              

How to Compile TypeScript

Open up your text editor and create a TypeScript file (for example, alphabetize.ts).

Write some JavaScript or TypeScript:

                allow sport = 'football';  let id = 5;                              

We can at present compile this down into JavaScript with the following command:

                tsc index                              

TSC will compile the code into JavaScript and output information technology in a file called index.js:

                var sport = 'football'; var id = five;                              

If you want to specify the name of the output file:

tsc alphabetize.ts --outfile file-name.js

If you want TSC to compile your lawmaking automatically, whenever you make a change, add the "watch" flag:

tsc alphabetize.ts -w

An interesting thing well-nigh TypeScript is that information technology reports errors in your text editor whilst you are coding, simply it will always compile your code – whether there are errors or not.

For example, the following causes TypeScript to immediately written report an fault:

                var sport = 'football'; var id = five;  id = '5'; // Error: Type 'string' is not assignable to  type 'number'.              

But if we try to compile this code with tsc index, the lawmaking will nevertheless compile, despite the fault.

This is an of import property of TypeScript: it assumes that the developer knows more. Even though there'due south a TypeScript error, information technology doesn't get in your way of compiling the code. It tells you lot at that place'due south an error, but it's up to you lot whether you do anything about it.

How to Ready Upwards the ts config File

The ts config file should be in the root directory of your project. In this file we tin specify the root files, compiler options, and how strict we want TypeScript to exist in checking our project.

First, create the ts config file:

tsc --init

You should now have a tsconfig.json file in the project root.

Here are some options that are good to be enlightened of (if using a frontend framework with TypeScript, near if this stuff is taken care of for you):

                {     "compilerOptions": {         ...         /* Modules */         "target": "es2016", // Alter to "ES2015" to compile to ES6         "rootDir": "./src", // Where to compile from         "outDir": "./public", // Where to compile to (usually the folder to be deployed to the web server)                  /* JavaScript Support */         "allowJs": true, // Allow JavaScript files to be compiled         "checkJs": true, // Type cheque JavaScript files and report errors                  /* Emit */         "sourceMap": true, // Create source map files for emitted JavaScript files (good for debugging)          "removeComments": truthful, // Don't emit comments     },     "include": ["src"] // Ensure only files in src are compiled }                              

To compile everything and watch for changes:

tsc -w

Note: when input files are specified on the command line (for example, tsc index), tsconfig.json files are ignored.

Types in TypeScript

Primitive types

In JavaScript, a primitive value is data that is non an object and has no methods. There are vii primitive data types:

  • string
  • number
  • bigint
  • boolean
  • undefined
  • null
  • symbol

Primitives are immutable: they can't be altered. Information technology is of import not to confuse a primitive itself with a variable assigned a archaic value. The variable may be reassigned a new value, but the existing value can't be inverse in the ways that objects, arrays, and functions tin exist contradistinct.

Here'due south an example:

                let name = 'Danny'; name.toLowerCase(); panel.log(proper noun); // Danny - the cord method didn't mutate the string  let arr = [one, three, 5, seven]; arr.pop(); console.log(arr); // [1, 3, 5] - the assortment method mutated the array  proper name = 'Anna' // Assignment gives the primitive a new (not a mutated) value                              

In JavaScript, all archaic values (apart from null and undefined) have object equivalents that wrap effectually the primitive values. These wrapper objects are String, Number, BigInt, Boolean, and Symbol. These wrapper objects provide the methods that allow the archaic values to be manipulated.

Back to TypeScript, we can set the type we desire a variable to be be adding : type (chosen a "type annotation" or a "blazon signature") later declaring a variable. Examples:

                let id: number = v; permit firstname: string = 'danny'; let hasDog: boolean = true;  let unit of measurement: number; // Declare variable without assigning a value unit = 5;                              

But information technology's normally best to not explicitly state the type, as TypeScript automatically infers the type of a variable (blazon inference):

                let id = 5; // TS knows it'south a number let firstname = 'danny'; // TS knows it's a string let hasDog = true; // TS knows it's a boolean  hasDog = 'yes'; // Mistake                              

We tin can likewise set a variable to be able to be a spousal relationship type. A union type is a variable that can be assigned more than than 1 type:

                let historic period: cord | number; age = 26; age = '26';                              

Reference Types

In JavaScript, most "everything" is an object. In fact (and confusingly), strings, numbers and booleans can exist objects if defined with the new keyword:

                let firstname = new String('Danny'); console.log(firstname); // String {'Danny'}                              

Just when we talk of reference types in JavaScript, we are referring to arrays, objects and functions.

Caveat: primitive vs reference types

For those that accept never studied primitive vs reference types, let's discuss the fundamental difference.

If a primitive blazon is assigned to a variable, we can call back of that variable as containing the archaic value. Each primitive value is stored in a unique location in memory.

If we take two variables, x and y, and they both comprise primitive data, so they are completely independent of each other:

Primitive data are stored in unique memory locations
X and Y both comprise unique independent archaic data
                let x = 2; let y = 1;  x = y; y = 100; console.log(10); // 1 (even though y changed to 100, x is withal one)                              

This isn't the case with reference types. Reference types refer to a memory location where the object is stored.

Reference types memory locations
point1 and point2 incorporate a reference to the accost where the object is stored
                let point1 = { ten: 1, y: 1 }; permit point2 = point1;  point1.y = 100; console.log(point2.y); // 100 (point1 and point2 refer to the same memory address where the point object is stored)                              

That was a quick overview of primary vs reference types. Check out this commodity if you need a more thorough explanation: Primitive vs reference types.

Arrays in TypeScript

In TypeScript, you can ascertain what type of information an array can contain:

                let ids: number[] = [one, 2, three, 4, v]; // can only contain numbers allow names: cord[] = ['Danny', 'Anna', 'Bazza']; // tin can just comprise strings allow options: boolean[] = [true, faux, simulated]; can only contain truthful or false allow books: object[] = [   { name: 'Fooled past randomness', author: 'Nassim Taleb' },   { name: 'Sapiens', author: 'Yuval Noah Harari' }, ]; // tin can just contain objects let arr: any[] = ['hello', 1, true]; // any basically reverts TypeScript dorsum into JavaScript  ids.push button(vi); ids.push('7'); // ERROR: Argument of blazon 'cord' is not assignable to parameter of type 'number'.              

Y'all tin can use marriage types to define arrays containing multiple types:

                let person: (string | number | boolean)[] = ['Danny', 1, true]; person[0] = 100; person[i] = {name: 'Danny'} // Error - person array tin't contain objects              

If you lot initialise a variable with a value, information technology's not necessary to explicitly state the type, as TypeScript will infer it:

                permit person = ['Danny', 1, true]; // This is identical to above example person[0] = 100; person[1] = { proper noun: 'Danny' }; // Error - person assortment can't contain objects              

There is a special blazon of array that tin exist defined in TypeScript: Tuples. A tuple is an array with stock-still size and known datatypes. They are stricter than regular arrays.

                let person: [string, number, boolean] = ['Danny', one, true]; person[0] = 100; // Error - Value at index 0 can merely be a cord                              

Objects in TypeScript

Objects in TypeScript must take all the correct properties and value types:

                // Declare a variable chosen person with a specific object type annotation let person: {   name: string;   location: string;   isProgrammer: boolean; };  // Assign person to an object with all the necessary properties and value types person = {   name: 'Danny',   location: 'UK',   isProgrammer: truthful, };  person.isProgrammer = 'Yes'; // ERROR: should exist a boolean   person = {   name: 'John',   location: 'U.s.', };  // ERROR: missing the isProgrammer property              

When defining the signature of an object, you volition usually utilise an interface. This is useful if we need to bank check that multiple objects have the same specific backdrop and value types:

                interface Person {   name: string;   location: cord;   isProgrammer: boolean; }  let person1: Person = {   name: 'Danny',   location: 'U.k.',   isProgrammer: truthful, };  let person2: Person = {   name: 'Sarah',   location: 'Germany',   isProgrammer: false, };                              

We can also declare office backdrop with function signatures. We can do this using erstwhile-school mutual JavaScript functions (sayHi), or ES6 arrow functions (sayBye):

                interface Oral communication {   sayHi(name: cord): cord;   sayBye: (proper name: string) => cord; }  let sayStuff: Voice communication = {   sayHi: function (name: string) {     render `Hi ${proper name}`;   },   sayBye: (name: cord) => `Bye ${proper noun}`, };  console.log(sayStuff.sayHi('Heisenberg')); // Howdy Heisenberg console.log(sayStuff.sayBye('Heisenberg')); // Adieu Heisenberg                              

Note that in the sayStuff object, sayHi or sayBye could be given an pointer office or a common JavaScript function – TypeScript doesn't care.

Functions in TypeScript

We can ascertain what the types the function arguments should be, as well equally the return type of the function:

                // Ascertain a role called circle that takes a diam variable of type number, and returns a cord function circle(diam: number): string {   render 'The circumference is ' + Math.PI * diam; }  console.log(circle(10)); // The circumference is 31.41592653589793              

The same role, only with an ES6 arrow office:

                const circumvolve = (diam: number): string => {   return 'The circumference is ' + Math.PI * diam; };  console.log(circle(10)); // The circumference is 31.41592653589793              

Notice how it isn't necessary to explicitly state that circumvolve is a function; TypeScript infers it. TypeScript also infers the render type of the role, so it doesn't need to be stated either. Although, if the office is big, some developers like to explicitly state the return type for clarity.

                // Using explicit typing  const circle: Function = (diam: number): string => {   return 'The circumference is ' + Math.PI * diam; };  // Inferred typing - TypeScript sees that circle is a part that e'er returns a string, and then no demand to explicitly country it const circumvolve = (diam: number) => {   return 'The circumference is ' + Math.PI * diam; };              

Nosotros tin add a question mark afterwards a parameter to make it optional. Likewise notice beneath how c is a union type that can be a number or string:

                const add = (a: number, b: number, c?: number | string) => {   console.log(c);    return a + b; };  console.log(add together(5, 4, 'I could pass a number, cord, or cypher here!')); // I could pass a number, string, or zip here! // 9                              

A function that returns nil is said to return void – a complete lack of any value. Below, the render blazon of void has been explicitly stated. But again, this isn't necessary equally TypeScript will infer it.

                const logMessage = (msg: string): void => {   console.log('This is the message: ' + msg); };  logMessage('TypeScript is superb'); // This is the message: TypeScript is superb              

If we desire to declare a function variable, simply not ascertain it (say exactly what information technology does), then use a office signature. Below, the function sayHello must follow the signature after the colon:

                // Declare the varible sayHello, and requite information technology a office signature that takes a string and returns goose egg. let sayHello: (name: string) => void;  // Ascertain the function, satisfying its signature sayHello = (name) => {   console.log('Hello ' + name); };  sayHello('Danny'); // Hello Danny                              

Dynamic (any) types

Using the any type, we can basically revert TypeScript dorsum into JavaScript:

                let historic period: any = '100'; age = 100; age = {   years: 100,   months: 2, };                              

It's recommended to avoid using the any type equally much equally you can, as it prevents TypeScript from doing its job – and can lead to bugs.

Type Aliases

Blazon Aliases tin reduce code duplication, keeping our lawmaking Dry. Below, we can run across that the PersonObject type alias has prevented repetition, and acts as a single source of truth for what data a person object should incorporate.

                type StringOrNumber = cord | number;  blazon PersonObject = {   name: string;   id: StringOrNumber; };  const person1: PersonObject = {   name: 'John',   id: 1, };  const person2: PersonObject = {   name: 'Delia',   id: two, };  const sayHello = (person: PersonObject) => {   return 'Hullo ' + person.proper noun; };  const sayGoodbye = (person: PersonObject) => {   return 'Seeya ' + person.name; };                              

The DOM and type casting

TypeScript doesn't have access to the DOM similar JavaScript. This means that whenever we attempt to admission DOM elements, TypeScript is never certain that they actually exist.

The beneath example shows the problem:

                const link = document.querySelector('a');  panel.log(link.href); // Fault: Object is possibly 'null'. TypeScript can't be sure the anchor tag exists, as it tin can't access the DOM              

With the non-naught assertion operator (!) we can tell the compiler explicitly that an expression has value other than null or undefined. This is tin be useful when the compiler cannot infer the type with certainty, but nosotros have more information than the compiler.

                // Hither nosotros are telling TypeScript that we are certain that this ballast tag exists const link = document.querySelector('a')!;  console.log(link.href); // www.freeCodeCamp.org              

Notice how we didn't take to state the blazon of the link variable. This is because TypeScript can clearly encounter (via Type Inference) that it is of type HTMLAnchorElement.

Just what if nosotros needed to select a DOM element by its class or id? TypeScript tin't infer the type, equally it could be anything.

                const form = document.getElementById('signup-form');  panel.log(form.method); // Fault: Object is possibly 'goose egg'. // ERROR: Property 'method' does not be on type 'HTMLElement'.                              

Higher up, we get ii errors. We need to tell TypeScript that we are sure form exists, and that we know information technology is of type HTMLFormElement. We practise this with type casting:

                const form = document.getElementById('signup-form') every bit HTMLFormElement;  console.log(form.method); // post              

And TypeScript is happy!

TypeScript too has an Outcome object built in. So, if we add a submit effect listener to our course, TypeScript will give us an fault if we telephone call whatsoever methods that aren't role of the Event object. Cheque out how cool TypeScript is – information technology can tell us when nosotros've made a spelling mistake:

                const form = certificate.getElementById('signup-form') as HTMLFormElement;  course.addEventListener('submit', (east: Event) => {   e.preventDefault(); // prevents the folio from refreshing    panel.log(east.tarrget); // Mistake: Property 'tarrget' does not be on type 'Event'. Did you mean 'target'? });                              

Classes in TypeScript

We can ascertain the types that each piece of data should exist in a class:

                form Person {   name: string;   isCool: boolean;   pets: number;    constructor(due north: string, c: boolean, p: number) {     this.name = n;     this.isCool = c;     this.pets = p;   }    sayHello() {     render `Hi, my proper noun is ${this.name} and I have ${this.pets} pets`;   } }  const person1 = new Person('Danny', false, 1); const person2 = new Person('Sarah', 'aye', vi); // Mistake: Argument of type 'cord' is not assignable to parameter of type 'boolean'.  console.log(person1.sayHello()); // Hullo, my name is Danny and I have 1 pets              

Nosotros could and then create a people array that only includes objects synthetic from the Person class:

                let People: Person[] = [person1, person2];              

Nosotros tin add admission modifiers to the properties of a class. TypeScript also provides a new access modifier called readonly.

                grade Person {   readonly name: string; // This property is immutable - it can only be read   private isCool: boolean; // Can just access or change from methods within this form   protected email: string; // Can access or modify from this class and subclasses   public pets: number; // Can access or modify from anywhere - including exterior the class    constructor(n: string, c: boolean, due east: cord, p: number) {     this.name = n;     this.isCool = c;     this.email = east;     this.pets = p;   }    sayMyName() {     console.log(`Your non Heisenberg, y'all're ${this.name}`);   } }  const person1 = new Person('Danny', false, 'dan@eastward.com', 1); panel.log(person1.name); // Fine person1.name = 'James'; // Error: read only console.log(person1.isCool); // Error: private property - only attainable inside Person course console.log(person1.email); // Error: protected belongings - just accessible within Person class and its subclasses console.log(person1.pets); // Public property - so no problem              

Nosotros can brand our code more concise by constructing class properties this way:

                class Person {   constructor(     readonly name: string,     private isCool: boolean,     protected email: cord,     public pets: number   ) {}    sayMyName() {     console.log(`Your not Heisenberg, you're ${this.name}`);   } }  const person1 = new Person('Danny', faux, 'dan@eastward.com', 1); console.log(person1.name); // Danny                              

Writing it the to a higher place style, the properties are automatically assigned in the constructor – saving us from having to write them all out.

Note that if nosotros omit the access modifier, past default the property will exist public.

Classes can as well be extended, just like in regular JavaScript:

                class Programmer extends Person {   programmingLanguages: string[];    constructor(     name: string,     isCool: boolean,     email: string,     pets: number,     pL: cord[]   ) {     // The super call must supply all parameters for base (Person) class, every bit the constructor is not inherited.     super(name, isCool, email, pets);     this.programmingLanguages = pL;   } }              

For more than on classes, refer to the official TypeScript docs.

Modules in TypeScript

In JavaScript, a module is  merely a file containing related lawmaking. Functionality can be imported and exported betwixt modules, keeping the code well organized.

TypeScript besides supports modules. The TypeScript files will compile down into multiple JavaScript files.

In the tsconfig.json file, change the following options to support modern importing and exporting:

                                  "target": "es2016",  "module": "es2015"              

(Although, for Node projects you very likely want "module": "CommonJS" – Node doesn't  yet support modern importing/exporting.)

At present, in your HTML file, alter the script import to be of type module:

                <script blazon="module" src="/public/script.js"></script>                              

We can at present import and export files using ES6:

                // src/hullo.ts export office sayHi() {   console.log('How-do-you-do there!'); }  // src/script.ts import { sayHi } from './hullo.js';  sayHi(); // Howdy there!                              

Note: always import as a JavaScript file, even in TypeScript files.

Interfaces in TypeScript

Interfaces define how an object should look:

                interface Person {   name: string;   age: number; }  part sayHi(person: Person) {   console.log(`How-do-you-do ${person.name}`); }  sayHi({   name: 'John',   age: 48, }); // Hi John                              

You tin can also define an object type using a type allonym:

                type Person = {   name: string;   age: number; };  function sayHi(person: Person) {   console.log(`Hi ${person.proper name}`); }  sayHi({   name: 'John',   age: 48, }); // Hi John              

Or an object blazon could be defined anonymously:

                function sayHi(person: { name: cord; historic period: number }) {   panel.log(`Hi ${person.name}`); }  sayHi({   proper noun: 'John',   age: 48, }); // Howdy John                              

Interfaces are very like to type aliases, and in many cases you can utilize either. The key stardom is that type aliases cannot be reopened to add new backdrop, vs an interface which is always extendable.

The following examples are taken from the TypeScript docs.

Extending an interface:

                interface Animal {   name: string }  interface Conduct extends Creature {   honey: boolean }  const carry: Bear = {   proper name: "Winnie",   love: truthful, }              

Extending a type via intersections:

                blazon Animal = {   name: string }  blazon Conduct = Brute & {   honey: boolean }  const carry: Carry = {   name: "Winnie",   honey: true, }              

Adding new fields to an existing interface:

                interface Animal {   name: string }  // Re-opening the Brute interface to add a new field interface Brute {   tail: boolean }  const dog: Animal = {   name: "Bruce",   tail: true, }              

Here's the central difference: a type cannot be changed after being created:

                type Animal = {   proper noun: string }  type Animal = {   tail: boolean } // Mistake: Duplicate identifier 'Animal'.              

As a rule of thumb, the TypeScript docs recommend using interfaces to ascertain objects, until you need to use the features of a type.

Interfaces can as well define function signatures:

                interface Person {   name: cord   age: number   speak(judgement: string): void }  const person1: Person = {   proper name: "John",   age: 48,   speak: sentence => console.log(sentence), }              

You may be wondering why we would use an interface over a class in the above example.

One advantage of using an interface is that it is only used past TypeScript, not JavaScript. This means that it won't become compiled and add together bloat to your JavaScript. Classes are features of JavaScript, then information technology would become compiled.

Also, a class is substantially an object mill (that is, a design of what an object is supposed to look like so implemented), whereas an interface is a structure used solely for type-checking .

While a class may have initialized properties and methods to help create objects, an interface substantially defines the properties and type an object tin can have.

Interfaces with classes

We can tell a class that it must contain certain properties and methods past implementing an interface:

                interface HasFormatter {   format(): string; }  class Person implements HasFormatter {   constructor(public username: cord, protected password: string) {}    format() {     return this.username.toLocaleLowerCase();   } }  // Must exist objects that implement the HasFormatter interface let person1: HasFormatter; let person2: HasFormatter;  person1 = new Person('Danny', 'password123'); person2 = new Person('Jane', 'TypeScripter1990');  console.log(person1.format()); // danny              

Ensure that people is an array of objects that implement HasFormatter (ensures that each person has the format method):

                let people: HasFormatter[] = []; people.push(person1); people.push(person2);              

Literal types in TypeScript

In addition to the full general types string and number, we can refer to specific strings and numbers in type positions:

                // Union blazon with a literal blazon in each position let favouriteColor: 'ruddy' | 'blue' | 'dark-green' | 'yellow';  favouriteColor = 'blueish'; favouriteColor = 'crimson'; // ERROR: Blazon '"red"' is not assignable to type '"red" | "blue" | "green" | "yellow"'.              

Generics

Generics allow you to create a component that can work over a variety of types, rather than a single ane, which helps to make the component more than reusable.

Let'south become through an example to show you what that means...

The addID function accepts whatsoever object, and returns a new object with all the backdrop and values of the passed in object, plus an id property with random value betwixt 0 and 1000. In short, it gives any object an ID.

                                  const addID = (obj: object) => {   let id = Math.floor(Math.random() * one thousand);    return { ...obj, id }; };  permit person1 = addID({ name: 'John', historic period: xl });  console.log(person1.id); // 271 console.log(person1.proper noun); // Fault: Belongings 'proper name' does not exist on blazon '{ id: number; }'.                              

As you lot can see, TypeScript gives an error when we endeavor to access the proper noun property. This is considering when we pass in an object to addID, we are not specifying what backdrop this object should have – so TypeScript has no thought what properties the object has (it hasn't "captured" them). So, the simply belongings that TypeScript knows is on the returned object is id.

So, how can nosotros pass in whatever object to addID, but all the same tell TypeScript what properties and values the object has? Nosotros can use a generic, <T> – where T is known as the blazon parameter:

                // <T> is just the convention - e.thou. nosotros could use <X> or <A> const addID = <T>(obj: T) => {   permit id = Math.floor(Math.random() * thou);    return { ...obj, id }; };              

What does this do? Well, now when nosotros laissez passer an object into addID, we have told TypeScript to capture the type – and so T becomes any type we laissez passer in. addID will at present know what properties are on the object we pass in.

But, we now accept a problem: anything can exist passed into addID and TypeScript volition capture the type and report no problem:

                let person1 = addID({ proper noun: 'John', age: 40 }); let person2 = addID('Sally'); // Pass in a cord - no problem  panel.log(person1.id); // 271 console.log(person1.name); // John  console.log(person2.id); panel.log(person2.name); // ERROR: Property 'name' does non exist on blazon '"Sally" & { id: number; }'.              

When nosotros passed in a string, TypeScript saw no result. It but reported an error when we tried to access the name property. So, we need a constraint: we need to tell TypeScript that only objects should be accustomed, past making our generic type, T, an extension of object:

                const addID = <T extends object>(obj: T) => {   let id = Math.floor(Math.random() * thou);    return { ...obj, id }; };  let person1 = addID({ name: 'John', age: 40 }); let person2 = addID('Sally'); // ERROR: Argument of type 'string' is not assignable to parameter of type 'object'.              

The mistake is defenseless straight abroad – perfect... well, non quite. In JavaScript, arrays are objects, so we can all the same go away with passing in an assortment:

                let person2 = addID(['Emerge', 26]); // Pass in an array - no problem  console.log(person2.id); // 824 console.log(person2.proper name); // Mistake: Belongings 'name' does not exist on type '(cord | number)[] & { id: number; }'.              

We could solve this by saying that the object statement should accept a name belongings with string value:

                const addID = <T extends { name: string }>(obj: T) => {   let id = Math.floor(Math.random() * k);    return { ...obj, id }; };  allow person2 = addID(['Emerge', 26]); // Mistake: statement should have a name belongings with string value              

The type tin also exist passed in to <T>, as beneath – but this isn't necessary most of the time, as TypeScript will infer it.

                // Below, we have explicitly stated what blazon the argument should be between the angle brackets. permit person1 = addID<{ name: string; age: number }>({ name: 'John', historic period: 40 });              

Generics permit y'all to accept blazon-safety in components where the arguments and return types are unknown ahead of time.

In TypeScript, generics are used when we want to depict a correspondence betwixt 2 values. In the above instance, the return blazon was related to the input type. We used a generic to describe the correspondence.

Another case: If we need a function that accepts multiple types, it is ameliorate to apply a generic than the any type. Below shows the upshot with using whatsoever:

                function logLength(a: any) {   console.log(a.length); // No error   render a; }  allow hi = 'How-do-you-do world'; logLength(hello); // 11  let howMany = 8; logLength(howMany); // undefined (but no TypeScript error - surely we desire TypeScript to tell the states we've tried to access a length holding on a number!)              

We could effort using a generic:

                function logLength<T>(a: T) {   console.log(a.length); // Mistake: TypeScript isn't certain that `a` is a value with a length property   return a; }              

At least we are now getting some feedback that we tin can use to tighten up our lawmaking.

Solution: utilize a generic that extends an interface that ensures every argument passed in has a length property:

                interface hasLength {   length: number; }  role logLength<T extends hasLength>(a: T) {   panel.log(a.length);   render a; }  let how-do-you-do = 'Hello earth'; logLength(hello); // xi  let howMany = eight; logLength(howMany); // Fault: numbers don't have length properties              

We could as well write a function where the argument is an assortment of elements that all take a length belongings:

                interface hasLength {   length: number; }  role logLengths<T extends hasLength>(a: T[]) {   a.forEach((element) => {     console.log(chemical element.length);   }); }  let arr = [   'This string has a length prop',   ['This', 'arr', 'has', 'length'],   { material: 'plastic', length: thirty }, ];  logLengths(arr); // 29 // 4 // 30              

Generics are an awesome feature of TypeScript!

Generics with interfaces

When nosotros don't know what blazon a sure value in an object will be alee of fourth dimension, we can use a generic to pass in the type:

                // The type, T, will be passed in interface Person<T> {   proper noun: string;   age: number;   documents: T; }  // Nosotros have to pass in the type of `documents` - an array of strings in this case const person1: Person<string[]> = {   name: 'John',   age: 48,   documents: ['passport', 'bank argument', 'visa'], };  // Again, nosotros implement the `Person` interface, and pass in the blazon for documents - in this case a string const person2: Person<string> = {   proper name: 'Delia',   age: 46,   documents: 'passport, P45', };              

Enums in TypeScript

Enums are a special feature that TypeScript brings to JavaScript. Enums allow u.s.a. to define or declare a collection of related values, that can be numbers or strings, as a prepare of named constants.

                enum ResourceType {   Volume,   AUTHOR,   Pic,   Manager,   PERSON, }  panel.log(ResourceType.Book); // 0 console.log(ResourceType.AUTHOR); // 1  // To start from 1 enum ResourceType {   BOOK = ane,   AUTHOR,   FILM,   Director,   PERSON, }  console.log(ResourceType.Book); // ane console.log(ResourceType.Author); // 2              

By default, enums are number based – they store cord values every bit numbers. Only they tin can besides be strings:

                enum Direction {   Upward = 'Upward',   Right = 'Right',   Down = 'Down',   Left = 'Left', }  panel.log(Direction.Right); // Right panel.log(Direction.Down); // Down              

Enums are useful when we take a prepare of related constants. For instance, instead of using non-descriptive numbers throughout your code, enums make code more readable with descriptive constants.

Enums can also forbid bugs, as when yous type the proper noun of the enum, intellisense will pop up and give you the listing of possible options that tin be selected.

TypeScript strict mode

Information technology is recommended to accept all strict type-checking operations enabled in the tsconfig.json file. This will cause TypeScript to report more errors, just will help prevent many bugs from creeping into your application.

                                  // tsconfig.json  "strict": true              

Let's talk over a couple of the things that strict manner does: no implicit whatever, and strict cipher checks.

No implicit any

In the function beneath, TypeScript has inferred that the parameter a is of any type. As you tin see, when we pass in a number to this function, and effort to log a name property, no fault is reported. Not good.

                office logName(a) {   // No error??   console.log(a.proper noun); }  logName(97);              

With the noImplicitAny option turned on, TypeScript will instantly flag an error if we don't explicitly state the type of a:

                // Error: Parameter 'a' implicitly has an 'whatever' type. role logName(a) {   console.log(a.name); }              

Strict zippo checks

When the strictNullChecks option is false, TypeScript effectively ignores cypher and undefined. This can lead to unexpected errors at runtime.

With strictNullChecks set to true, naught and undefined have their own types, and you'll get a type error if you assign them to a variable that expects a concrete value (for example, string).

                permit whoSangThis: string = getSong();  const singles = [   { song: 'bear on of grey', creative person: 'grateful dead' },   { song: 'paint it black', artist: 'rolling stones' }, ];  const single = singles.discover((southward) => s.song === whoSangThis);  panel.log(single.creative person);                              

Above, singles.notice has no guarantee that it will find the vocal – but we have written the code equally though it e'er volition.

Past setting strictNullChecks to true, TypeScript will heighten an fault because nosotros oasis't made a guarantee that single exists before trying to apply information technology:

                const getSong = () => {   return 'vocal'; };  permit whoSangThis: string = getSong();  const singles = [   { vocal: 'touch of grey', artist: 'grateful expressionless' },   { vocal: 'paint it black', creative person: 'rolling stones' }, ];  const single = singles.find((s) => s.vocal === whoSangThis);  console.log(single.artist); // Fault: Object is maybe 'undefined'.              

TypeScript is basically telling usa to ensure single exists earlier using it. Nosotros need to bank check if information technology isn't null or undefined first:

                if (single) {   console.log(unmarried.artist); // rolling stones }                              

Narrowing in TypeScript

In a TypeScript plan, a variable can move from a less precise blazon to a more precise blazon. This process is called type narrowing.

Here's a simple example showing how TypeScript narrows down the less specific type of string | number to more specific types when we use if-statements with typeof:

                role addAnother(val: string | number) {   if (typeof val === 'cord') {     // TypeScript treats `val` as a string in this block, so we tin use cord methods on `val` and TypeScript won't shout at us     render val.concat(' ' + val);   }    // TypeScript knows `val` is a number hither   render val + val; }  console.log(addAnother('Woooo')); // Woooo Woooo panel.log(addAnother(20)); // 40                              

Some other example: below, nosotros have defined a union type called allVehicles, which tin can either be of type Plane or Railroad train.

                interface Vehicle {   topSpeed: number; }  interface Train extends Vehicle {   carriages: number; }  interface Plane extends Vehicle {   wingSpan: number; }  type PlaneOrTrain = Plane | Train;  role getSpeedRatio(v: PlaneOrTrain) {   // In here, we want to return topSpeed/carriages, or topSpeed/wingSpan   console.log(v.carriages); // ERROR: 'carriages' doesn't exist on type 'Aeroplane' }                              

Since the office getSpeedRatio is working with multiple types, we need a manner of distinguishing whether 5 is a Aeroplane or Railroad train. We could do this by giving both types a mutual distinguishing property, with a literal cord value:

                // All trains must now have a type property equal to 'Railroad train' interface Railroad train extends Vehicle {   type: 'Train';   carriages: number; }  // All trains must now have a type belongings equal to 'Plane' interface Plane extends Vehicle {   type: 'Plane';   wingSpan: number; }  type PlaneOrTrain = Plane | Train;              

Now we, and TypeScript, can narrow downwardly the blazon of v:

                function getSpeedRatio(v: PlaneOrTrain) {   if (v.type === 'Train') {     // TypeScript now knows that `five` is definitely a `Train`. Information technology has narrowed down the type from the less specific `Aeroplane | Train` type, into the more specific `Train` type     return v.topSpeed / v.carriages;   }    // If information technology'due south not a Railroad train, TypeScript narrows downward that `v` must be a Aeroplane - smart!   return v.topSpeed / five.wingSpan; }  allow bigTrain: Train = {   type: 'Train',   topSpeed: 100,   carriages: 20, };  console.log(getSpeedRatio(bigTrain)); // v              

Bonus: TypeScript with React

TypeScript has full support for React and JSX. This means we tin use TypeScript with the 3 near common React frameworks:

  • create-react-app (TS setup)
  • Gatsby (TS setup)
  • Next.js (TS setup)

If you require a more custom React-TypeScript configuration, you could setup Webpack (a module bundler) and configure the tsconfig.json yourself. But almost of the time, a framework will do the job.

To setup up create-react-app with TypeScript, for example, but run:

                npx create-react-app my-app --template typescript  # or  yarn create react-app my-app --template typescript              

In the src binder, we tin at present create files with .ts (for regular TypeScript files) or .tsx (for TypeScript with React) extensions and write our components with TypeScript. This volition then compile down into JavaScript in the public folder.

React props with TypeScript

Below, we are proverb that Person should be a React functional component that accepts a props object with the props proper noun, which should exist a string, and age, which should be a number.

                // src/components/Person.tsx import React from 'react';  const Person: React.FC<{   name: cord;   age: number; }> = ({ name, age }) => {   return (     <div>       <div>{proper noun}</div>       <div>{age}</div>     </div>   ); };  export default Person;                              

Just most developers prefer to utilise an interface to specify prop types:

                interface Props {   name: string;   age: number; }  const Person: React.FC<Props> = ({ name, age }) => {   return (     <div>       <div>{name}</div>       <div>{age}</div>     </div>   ); };              

We can then import this component into App.tsx. If nosotros don't provide the necessary props, TypeScript will requite an mistake.

                import React from 'react'; import Person from './components/Person';  const App: React.FC = () => {   return (     <div>       <Person name='John' historic period={48} />     </div>   ); };  export default App;                              

Here are a few examples for what we could have as prop types:

                interface PersonInfo {   name: string;   age: number; }  interface Props {   text: string;   id: number;   isVeryNice?: boolean;   func: (proper name: cord) => string;   personInfo: PersonInfo; }              

React hooks with TypeScript

useState()

We can declare what types a country variable should exist by using angle brackets. Below, if we omitted the angle brackets, TypeScript would infer that greenbacks is a number. Then, if want to enable it to also be null, we have to specify:

                const Person: React.FC<Props> = ({ name, age }) => {   const [cash, setCash] = useState<number | goose egg>(i);    setCash(nada);    return (     <div>       <div>{proper noun}</div>       <div>{historic period}</div>     </div>   ); };              

useRef()

useRef returns a mutable object that persists for the lifetime of the component. We can tell TypeScript what the ref object should refer to – below nosotros say the prop should be a HTMLInputElement:

                const Person: React.FC = () => {   // Initialise .current belongings to null   const inputRef = useRef<HTMLInputElement>(null);    return (     <div>       <input type='text' ref={inputRef} />     </div>   ); };              

For more information on React with TypeScript, checkout these awesome React-TypeScript cheatsheets.

Useful resources & further reading

  • The official TypeScript docs
  • The Net Ninja'due south TypeScript video series (crawly!)
  • Ben Awad's TypeScript with React video
  • Narrowing in TypeScript (a very interesting characteristic of TS that yous should learn)
  • Function overloads
  • Primitive values in JavaScript
  • JavaScript objects

Thanks for reading!

Hope that was useful. If you made it to here, yous now know the main fundamentals of TypeScript and can showtime using information technology in your projects.

Again, yous can as well download my 1-page TypeScript crook canvas PDF or order a physical poster.

For more than from me, you lot can observe me on Twitter and YouTube.

Cheers!



Learn to code for complimentary. freeCodeCamp's open source curriculum has helped more than than 40,000 people become jobs as developers. Get started

Can I Learn Typescript Without Javascript,

Source: https://www.freecodecamp.org/news/learn-typescript-beginners-guide/

Posted by: wilsontheyind.blogspot.com

0 Response to "Can I Learn Typescript Without Javascript"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel