TypeScript for Beginners
Tags: javascript, typescript, beginners, webdev
Published: 2022-05-21 13:08:46

## A quick view of the everyday types.

TypeScript has been one interesting thing amongst others that have happened to the JavaScript language in recent years. The superset script has indeed made programming in JavaScript not only safer but much more interesting and approachable (especially to developers coming from languages that use compilers).

It goes without saying that the most errors in JavaScript is the _type errors_. JavaScript inherently does not provide a type checking system for its code. so one big implication is that developers tend to provide a different value when a certain kind of value was expected.

This article will cover JavaScript's common data types and the equivalent ways to describe and typecheck them using TypeScript. It will not be touching the advanced parts (not necessarily) of TypeScript. What this article will show is the building block of TypeScript code.

Let's go...

_This article is a simplified version of the [everyday types](https://www.typescriptlang.org/docs/) from the TypeScript docs and assumes you know the fundamentals of javascript._

## Installing TypeScript.
TypeScript can be installed in 3 ways: through the NPM registry, as a Visual Studio extension, and through the .NET package registry (NuGet).

You can choose any routes you want for the installation. I have NodeJS installed so that's what I'll be using.

Visit [here](https://www.typescriptlang.org/download) to download TypeScript.

If you want to jump straight into code while following, you can use [this](https://playcode.io/typescript) TypeScript playground.

## Everyday Types

The essence of TypeScript is to provide make for what the language lacked the most –– a static typechecker. A typechecker runs before the actual code runs to ensure all data types are correct (typechecked) and are used as they should in a program.

### Annotating Primitives
JavaScript has three basic [primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) data types: `string`,`number` and `boolean`. These form the basic level types in the language.

- String represent text like `"Hi Romeo"`.
- Number represents a number like `5`. Every digit in JavaScript is represented as a `number`. There aren't special representations like `int` and `float`
- Boolean represents two values, `true` and `false`.

Below is the representation of primitive in JavaScript and the equivalent in TypeScript.

```javascript
// JavaScript
const name = "Romeo";
const age = 23;
const isChristian = true;
```

```typescript
// TypeScript
const name: string = "Romeo";
const age: number = 23;
cont isChrisian: boolean = true;
```
> <small style="color:orange;"}>NOTE:</small>
><br />  TypeScript is capable of inferring types automatically in your code. For example, the type of a variable is inferred based on the type of its initializer:

> `let myName = "Alice";`
> No type annotation needed -- 'myName' inferred as type
'string'

### Arrays
This represent types of an array, say a string of names like `["Romeo", "Waan', "Peter"]`. you can use the `string[]` syntax to do so. It works for other types as well, like numbers (`number[]`).

```javascript
// JavaScript
const names = ["Romeo", "Waan", "Maxwell", "Peter"];
const numbers = [23, 5.5, 42, 32];
```

```typescript
// Typescript
const names: string[] = ["Romeo", "Waan", "Maxwell", "Peter"];
const numbers: number[] = [23, 5.5, 42, 32];
```
### Any
The `any` type is special to TypeScript and will cause a value not to be validated by TypeScript for its type. In other words, it won't be typechecked.

When a value has the `any` type, it and its property can be accessed and manipulated as you normally would in JavaScript without it being typechecked. That means it can be assigned to (or from), called as function and its property, which in turn has a type of `any`, can be accessed.

```typescript
// Typescript
let someVariable: any = {name: "Romeo"};
someVariable.bar = 100;
someVariable = (n=10) => n;
someVariable();
```

> <small style="color:orange;"}>NOTE:</small>
> <br /> The `any` type can be disabled using the `noImplicitAny ` flag. You'll learn more about TypeScript configuration as we proceed.

<!-- Describe `noImplicitAny` in `.ts` configutation file. -->

### Functions

Function annotation in TypeScript is of two types: Parameter Type Annotation and Return Type Annotation.

#### Parameter Type Annotation
When you declare a function you can annotate the parameters to indicate the types of parameters the function expects. parameter type annotation comes after the parameter is declared, like so:

```javascript
// JavaScript
function greetRomeo(name) {
	console.log("Hey Romeo, I'm " + name);
}
```

```typescript
// TypeScript
function greetRomeo(name: string) {
	console.log("Hey Romeo, I'm " + name);
}
```
Any argument passed to the `greetRomeo` function will be checked. In the case above, the `name` parameter expects a string argument, anything else, say a number, will show an error.

> <small style="color:orange;"}>NOTE:</small>
> 
> TypeScript is strict about the number of parameters passed to a function. Even without annotation, it'll check that you passed in the right number of arguments.

#### Return Type Annotation

You can annotate the returned value from a function. Anything value return that doesn't match type annotated will be flagged for error.

```javascript
JavaScript

function getAge() {
	return 23
}
```

```typescript
TypeScript

function getAge(): number {
	return 23
}
```

Typescript can infer the return type based on the value return. Annotating the type value is mostly for documentation purposes. 

Personally, I take documentation quite seriously. Fellow developers shouldn't have to peel their scalp to understand what codebase does.

#### Anonymous function

When a function is passed as a callback (most often anonymous functions), TypeScript can determine how that function will be called thus inferring the parameter type for such functions.

```typescript
// No type annotations here, but TypeScript can spot the bug
const names = ["Romeo", "Waan", "Peter"];
 
// Contextual typing for function
names.forEach(function (s) {
  console.log(s.toUppercase()); // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});
 
// Contextual typing also applies to arrow functions
names.forEach((s) => {
  console.log(s.toUppercase()); // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});
```

Notice that the `s` parameter for both functions –– pure and arrow –– is not annotated, yet TypeScript could infer the correct type because it knows how the anonymous function will work on an array in that context. This is called _contextual typing_ because the _context_ where the function is used is known to TypeScript thus it (TypeScript) can infer the type the function should have.

### Object types
Besides primitives, a common form of data type you deal with as a javascript developer is an Object; this is any JavaScript data type with properties and values.

To define an object type,  you list the properties and their types. For instance, here's a function that takes an object as a parameter.

```javascript
// JavaScript

function romeosProfile(profile){
	if (typeof profile === "Object") {
		console.log("Romeo's age is " + profile.name);
		console.log("Romeo's height is " + profile.height);
	}
}
romeosProfile({name: "Romeo", age: 23});
```

```typescript
// TypeScript

// The parameter's type annotation is an object type
function romeosProfile(pt: {name: string, age: string}){
	console.log("Romeo's age is " + pt.name);
	console.log("Romeo's height is " + pt.height);
}
romeosProfile({name: "Romeo", age: 23});
```
The parameter of the function is annotated as an object type. the further annotation of the object type itself is optional which, if done so, will have a type of `any`.

#### Optional properties

Object types can specify some optional properties by appending a `?` after the property name.

```typescript
function romeosProfile(obj: {name: string, age?: number}) {
  // ...
}

// Both OK
printName({ name: "Romeo" });
printName({ name: "Romeo",	age: 23 });
``` 

Say you didn't provide an argument for the optional parameter and you accessed it, it will return `undefined` because the property doesn't exist. When reading from an optional property, make sure to check that it's not `undefined`.

```typescript
function romeosProfile(pt: {name: string, age?: number}) {

  // Error - might crash if 'pt.age' wasn't provided!
  console.log(pt.age);
  
  if (pt.age !== undefined) {
    // OK
    console.log(pt.age);
  }
 
  // A safe alternative using modern JavaScript syntax:
  console.log(pt.age?);
}
```

## Combining Types in TypeScript

The type system in TypeScript allows you to combine and form new types from existing ones.

### Union Type

A _union_ type is a type formed by combing two or more other types that represent values of any of the existing types. the combined types are referred to as the union's members.

Here is a function that accepts a string and a number as its parameter.

```typescript
function printAge(age: number | string) {
	console.log("i'm " + " years old");
}
printAge(23) // I'm 23 years old
printAge("23") // I'm 23 years old
printAge({age: 23}) // Error
```

### Working with Union Type

TypeScript will only allow an operation if it's valid (intersection of type property) for either of the union members. For instance, you can't perform a `toUpperCase` operation on a `string | number` union and that's because the operation is only valid for value with a type of `string` but the type value could be a `number`.

To get around a union type whose property may not intersect, you use _Narrowing_ –– to "... deduce a more specific type for a value based on the structure of the code."

For instance, using a conditional to let TypeScript know that an operation for a certain type.

```typescript
function printAge (age: number | string) {
	if (typeof age === "string") {
		console.log(age.toUpperCase());
	} else {
		// age is type of 'number'
		console.log(age)
	}
}
```

or if member of union type is an array:

```typescript
function meetFriends(x: string[] | string) {
	if (Array.isArray(x)) {
		console.log("Hello, " +  x.join(" and ") + ". Nice to meet you all!")
	} else {
		// is a string and not an array
		console.log("Hello " + x);
	}
}
```

As indicated earlier, members of a union type whose value share (intersect) properties don't need narrowing:

```typescript
function getFirstTime(x: number[] | string) {
	return x.slice(0, 3);
}
```

### Type Aliases

Type aliases allow you to name a type and use it more than once by only referring to it by name. It is a name for any type.

```typescript
// Object aliase type
type Profile = {
	name: string,
	age: number
}

// Union Type aliase type
type ID = number | string;
```

With the `Profile` type alias above, you can pass it as a parameter type for a function by simply referring to the name (`Profile`).

```typescript
// Type Profile creates a type structure to be used as parameter type
function getProfile(pt: Profile) {
	console.log("Name: " + pt.name);
  	console.log("Age: " + pt.age);
}
getProfile({name: "Romeo", age: 23});
```

### Interfaces

Interfaces are similar to types aliases, and the two can be used interchangeably to create a named type for an object. The only difference is that a type alias can't be added new fields when once created, compared to an interface that is opened to be added more fields.

```typescript
// Object interface
interface Profile = {
	name: string,
	age: number
}

// Valid and will be combined with above interface
interface Profile = {
	eyeColor: string
}

// Type Profile creates a type structure to be used as parameter type
function getProfile(pt: Profile) {
	console.log("Name: " + pt.name);
  	console.log("Age: " + pt.age);
}
getProfile({name: "Romeo", age: 23});
```

It is generally preferred to use `interface` when creating a named type for objects, just in case you'd want to add to the existing fields.

```typescript
interface Profile = {
	name: string,
	age: number,
	eyeColor: string
}


// Object takes the structure of defined interface using Type Assertion
const RomeosProfile: <Profile> {
	name: "Romeo",
	age: 23,
	eyeColor: "black"
}
```

### Type Assertions

Type assertion allows you to declare (assert) a type on a variable so that the compiler won't infer it at runtime. This is because you, as the programmer, could have more information about the type of value that TypeScript can't or at least, it'll infer something not quite right.

Type assertion is similar to typecasting in other languages like C++, C# and java except that there is no runtime effect (all assertions are removed by the compiler) in TypeScript.

for instance, if you're accessing the DOM using `document.getElementbyId`, TypeScript understands that it'll return an `HTMLElement`, but you might know specifically that it'll be an `HTMLCanvasElement`.

You can use a type assertion to specify that:

```typescript
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
```

You can use an angle-bracket to achieve the same effect.

```typescript
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");
```

Another instance is asserting an object, similar to the one you saw earlier.

```typescript
interface Creator { 
    name: string; 
    code: number; 
}

// Using the `as` keyword
let person = {} as Creator; // Ok
person.name = "Romeo";
person.age = 23;
 

// Using angle-bracket
let person = <Creator> {}; // ok
person.name = "Romeo";
person.age = 23;
```


> <small style="color:orange;"}>NOTE:</small>
> 
> When dealing with TypeScript in React (`.tsx` format), only the `as` keyword is allowed because using angle-brackets in JSX will create a conflict in the React code.


### Literal Types

With literal types, you can create and refer to specific strings and numbers in type positions.

For instance, a variable with a specific string type:

```typescript
let x: "hello" = "hello";
x = "Howdy"; // Type '"howdy"' is not assignable to type '"hello"'.
```

When combined to form a union, literals can be used to create complex and useful type structures in type positions.

for instance, a function with a second parameter that only accepts certain values.

```typescript
function creator(age: number, name: "Romeo" | "Waan" | "Peter"): void {
    console.log(alignment);
};

creator(23,"middle"); // Argument of type '"middle"' is not assignable to parameter of type '"Romeo" | "Waan" | "Peter

creator(23, "Waan") // No error, will log `Waan`
```

Also, a function that can only return numerical literals:

```typescript
function compare(a: string, b: string): 1 | -1 | 0 {
	return a === b ? 0 : a < b ? 1 : -1;
}

compare("23", "34"); // No error, will log 1;
```

Literal types can be combined with non-literal types too.

```typescript
interface Option {
	width: number;
}

function configure(x: Options | "auto") {
  // ...
}

configure({ width: 100 }); // No error
configure("auto"); // No error
configure("automatic"); // Error: Argument of type '"automatic"' is not assignable to parameter of type 'Options | "auto"'.
```

Although we didn't use it, the boolean (`true` and `false`) literal type can be used to achieve a similar concept as the ones above.

> <small style="color:orange;"}>NOTE:</small>
>  
> The `boolean` type is actually an alias that represents the union of `true` and `false`.

<br />

Alright, that's about it for the _everyday types_ you'll encounter while using or reading TypeScript code. Before I round up things, let's look into configuring TypeScript using the _tsconfig.json_. file


## TS Config

The TypeScript configuration file is `tsconfig.json` which sits at the root of the project. It's automatically created when the TypeScript is first initialized. The file specifies the root files and compiler options for the project.

### Using the `.tsconfig` to compile project.

There are two ways you can do this:

- By invoking the `tsc` CLI command with no input files, in which case the compiler uses the `tsconfig.json` (starting from the current directory and going the directory chain) file to look for the project file to compile.
- By invoking the `tsc` CLI command with a `--project` or `-p` option that specifies the directory to the `.tsconfig.json` file containing the configuration.

When input files are specified on the command line, the ones specified in the `tsconfig.json` files are ignored.

Here is a [reference](https://www.typescriptlang.org/tsconfig) to the configuration options you can make as fitting to a project.


## Conclusion.

The basis of this article is to get beginners started with building blocks of TypeScript code. The TypeScript documentation answers questions this article fails to provide. Do check [it](https://www.typescriptlang.org/docs/) out.

Let's connect on [Twitter](https://twitter.com/_romeopeter), I hear that my tweets are quite intriguing.

Cheers!