TypeScript, why is so important?

Why TypeScript is so important?


Why do types exist?

Well-known classical programming languages, such as Pascal, C, C++, etc., are strongly typed, which means that they must set stricter typing rules at compile time.

Every time you declare variables or function parameters, you must specify their type before using them. The reason behind this concept goes back a long way, to what is called type theory in order to ensure that programs make sense.

Hardware cannot distinguish between types. Types can be thought of as human abstractions that allow programmers to achieve a higher level of thinking and make code more concise.

In addition, from the compiler’s point of view, types have some advantages, such as ease of optimization. Type checking at compile time allows the compiler to execute machine instructions more efficiently. Security is another important consideration, and a strong typing system can help the compiler detect errors early.

With the advent of newer interpreted languages like Basic, JavaScript, PHP, Python, and so on, they do type checking at run time. Programmers can eliminate the need to compile their code, languages become more flexible and intelligent, and can do type detection based on context and data.

Return to the beginner’s mind

Instead of starting a new debate about strong versus weak typing, we need to understand that every language was designed and created for a specific purpose, and no one expected scripting languages like JavaScript to become so popular and widely used to develop business applications.

Adding strong typing capabilities to weakly typed languages like JavaScript not only helps development teams write clean self-explanatory code, but also solves a fundamental problem: catching type errors at compile time before runtime.

What is TypeScript?

JavaScript is an interpreted or dynamically compiled language, where developers do not need to compile code before running programs. Because we call TypeScript a JavaScript type superset, we mean that it gives developers a new syntax for adding types to a weakly typed language like JavaScript.

For example, when we declare a variable in JavaScript, we don’t need to specify a type. However, declaring variables in TypeScript requires a type. You can also assign values without setting the type.

let isDone: boolean
let decimal: number
let big: bigint
let color: string
let name = "John"
Copy the code

Unlike JavaScript(.js), TypeScript file suffixes use.ts extensions. Browsers do not recognize.ts files, so you must convert the TS code to JavaScript code before using them. This conversion process is called translation, and there are minor differences between compilation and translation:

  • Compilation is the conversion of source code into another language
  • Translation is the translation of source code into another language at the same level of abstraction

To be honest, I have to clarify this concept, because I have come across these two confusing concepts many times. However, for ease of reading, even the official TypeScript documentation has always referred to preprocessing as compilation.

The installation

We can install TypeScript using NPM and YARN

yarn add typescript
Copy the code

or

npm install typescript
Copy the code

We can then compile the TS file using the TSC command.

npx tsc
Copy the code

configuration

Create a new TS file in our project and then compile with TSC on the command line. Let’s create a new file called app.ts.

function add(num1: number, num2: number) :number {
  return num1 + num2
}
Copy the code

Then execute from the command line:

npx tsc app.ts
Copy the code

A file named app.js is generated with the following content.

function add(num1, num2) {
  return num1 + num2
}
Copy the code

But there is an easier way. The simplest way is to create a tsconfig.json file in your JS root directory and let the compiler execute through this configuration.

{
  "compilerOptions": {
    "target": "es6",
    "rootDir": "./src",
    "outDir": "./dist",
    "module": "commonjs",
    "removeComments": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}
Copy the code

The configuration file is divided into sections, as follows we can see that a basic configuration file has the following options:

  • Target: It determines the compiled JS version: ES3,ES5,ES6…
  • RootDir: This determines the root directory of your source code (.ts files)
  • OutDir: This determines the output directory of compiled JS files
  • Module: it sets the application’s module system: CommonJS,UMD,AMD…
  • RemoveComments: Removing comments when compiling code is considered a best practice
  • Include: This determines the directory where the source code is located
  • Exclude: Determines which files and folders need to be excluded during compilation

After defining new TypeScript configuration files, we can create multiple new TypeScript files in the SRC folder. Then, we can compile the files and put the generated files into the output folder by simply running NPX TSC on the command line.

We can also specify a TSC task in package.json, or even use the Watch option to make the file run TSC automatically after being changed.

There are several ways to set up TypeScript, depending on the technology and type of project you’re using. In this article, we won’t show you all possible configuration options, but we encourage you to check out the official documentation for more options.

How do I use TypeScript?

TypeScript is a tool that helps developers put tighter constraints on data types during software development. This must be combined with other good practices such as local variable declarations using let or const instead of var as appropriate.

The base type

Let’s review the types that TS provides.

Boolean, Number, String

Basic types are declared as follows:

let isDone: boolean = false
let decimal: number = 6
let hex: number = 0xf00d
let binary: number = 0b1010
let octal: number = 0o744
let big: bigint = 100n
let color: string = 'blue'
Copy the code

Arrays

Array types can be written in two ways:

let list: number[] = [1.2.3]
Copy the code

or

let list: Array<number> = [1.2.3]
Copy the code

tuples

So if we want to create an array where the first element is a string and the second element is a number or something like that, we can use a Tuple.

let x: [string.number]
x = ['hello'.10]
Copy the code

It is important to understand that TS exerts strict control over types and the order in which they are declared, so based on the above definition, the following code will report an error.

x = [10.'hello']  // WRONG
Copy the code

The enumeration

Like other languages (such as C or C++), TypeScript has enumerated types that declare multiple constants. Unlike other languages, TS enumerations are more flexible.

enum Color {
  Red,
  Green,
  Blue
}

let c: Color = Color.Green
Copy the code

Enumeration starts at 0, so Red = 0, Green = 1, Blue = 2, but in TS, you can change the order in the following way:

enum Color {
  Red = 1,
  Green,
  Blue
}
Copy the code

Or assign a different number to each constant

enum Color {
  Red = 2,
  Green = 6,
  Blue = 5
}
Copy the code

You can even assign a string value to each constant

enum Color {
  Up = "Up",
  Down = "Down",
  Left = "Left",
  Right = "Right"
}
Copy the code

Special type

Now that we know how to define primitive types, adding enhanced type validation to weakly typed languages can have a huge impact in a number of ways.

For example, we are interacting with the DOM and want to get a value from an HTML element. We can specify the type of the element, but before we can get the value from the element, we have to make sure that it exists.

const elem = document.getElementById('elementId')! as HTMLInputElement
Copy the code

The final exclamation point tells TS that although TS cannot be certain that the value exists on the element, we can accept the risk.

Another interesting example is when we need to specify that a function may take a string or a number as an argument; in other words, we can pass a string or a number as an argument.

For this scenario, we can use the pipe (|) merge all may receive type:

function combine(a: number | string, b: number | string) :void {
  //logic to validate types and perform operations
}
Copy the code

This pipe can also be used to specify special strings as parameters

function foo(color: 'yellow' | 'brown'){... }Copy the code

In the above example, the function must accept either yello or Brown as the string argument.

The return type of a function is also important. If we want to create a function that throws an error, what type is its return value?

As in this example, TS has a type called: never. This type means it will not happen. However, it is often used as a function to throw exceptions.

function error(msg: string) :never {
  throw new Error('msg')}Copy the code

In addition, functions that do not return should be declared with void.

function message(msg: string) :void {
  console.log('msg')}Copy the code

If we do not know what type of data is available, we can use the unknown keyword. In the example below, TypeScript does not control its type, but type validation must be done before assignment to another type.

let input: unknown

/before assigning it we should check its type
if(typeof input === "string") {
  let name: string = input
}
Copy the code

In addition to type checking before assignment, we can also cast its type to a type we know. Casts in TypeScript are as follows:

let myinput: unknown
let mylength: number = (<string>myinput).length
Copy the code

or

let myinput: unknown
let mylength: number = (myinput as string).length
Copy the code

There are situations where we don’t want TS to do type checking, such as when we use an external library that we can’t control, or when we need to define a function that might return an arbitrary type. For these cases, we can use any.

declare function getValue(key: string) :any
const str: string = getValue('test')
Copy the code

interface

Like other languages, interfaces are related to defining types, which must be followed when creating objects of interface type.

So, let’s assume that a function accepts a User object. We can create an interface to constrain the structure or rules of the object before using it.

interface User {
  name: string
  age: number
}

function displayPersonalInfo(user: User) {
  console.log(`Name: ${user.name} - Age: ${user.age}`)}Copy the code

When you create an interface, you can add modifiers like? Indicates that the property may be null, or you can use the readonly keyword to set an unmodifiable property.

interfaceSquare { color? :stringwidth? :number
}

interface Point {
  readonly x: number
  readonly y: number
}

let square: Square = {
  width: 14,}Copy the code

Readonly, by the way, is an interesting keyword that can be applied to other types. For example, there is a ReadonlyArray definition that allows a developer to create an array whose elements cannot be modified.

let a: number[] = [1.2.3.4]
let ronumbers: ReadonlyArray<number> = a

ronumbers[0] = 4 //WRONG! It cannot be assigned

//But it could be used for iterating over its values for reading purposes
for (const num of ronumbers) {
  console.log(num)
}
Copy the code

The generic

Finally, I’ll highlight one of the most critical features of object-oriented languages: generics, which also exist in TypeScript.

Reusable components are the foundation of every modern strongly typed language, as is JavaScript with its introduction, and we must give developers a way to define functions that have the same processing logic for different types of data.

This concept will be familiar to anyone who has worked with things like C++,C#,Kotlin,Java, and even Rust.

For other developers, a generic is a way of declaring an array, class, or function that uses a type they don’t know about.

The use of generics is a pair of <> that can contain any letters in between, which are used as tokens in later implementation logic and replaced by the actual type when the definition occurs.

function myMax<T> (x: T, y: T) :T {
  return x > y ? x : y
}

const intMax = myMax<number> (12.50)

console.log(intMax)
Copy the code

In the above example, we define a function that compares two values and returns the largest one. Note that the actual type (number) is passed in later.

conclusion

We can summarize TypeScript as a statically typed validation language that adds a layer of logic to the JavaScript front-end language to make it more robust. On closer inspection, we can also see how most languages add similar features: functional programming, lambda functions, strong typing, immutable variables, and so on.

This is a good sign, because it shows that the software industry is maturing and getting better for newcomers and newcomers.