TypeScript introduction part III - generics & stuff
Tags: typescript, javascript, tutorial
Published: 2019-03-13 14:47:47

**This post is taken from [my blog](https://areknawo.com), so be sure to check it out for more up-to-date content 😉**

Here, we are continuing  **the grand TypeScript introduction**. If you haven't already, be sure to check out  **[part I](https://areknawo.com/typescript-introduction/)**  and  **[II](https://areknawo.com/typescript-introduction-pt2/)**  for getting started with TS and knowing what's going on in this tutorial. 😉 In this article, we're going to finally explore  **generics**, some  **complex types**  and  **declaration files**. After this part of the series, you should most likely have enough knowledge about TS to write really complex stuff. So, yup,  **enjoy!**  🙂

# Generics

Let's start with something big!  **Generics**, because we'll be talking about them, are really important in TypeScript and some other statically-typed languages that include them. But, what are they exactly?

It can be safely assumed that the word generics has been created from the word general, which in this context means  **something same**. Take a look at the function below.

```typescript
function myFunction(arg: any): any {
    return arg;
}

```

Our function takes an argument of any type and simply returns it (I know, not really useful 😅). As we already know, any type isn't very type-safe. It also doesn't indicate, that the return type is the same as argument type (although that can be read from the code, but not to the compiler). We would like to indicate that these types are exactly the same. No unions, aliases and other stuff - strictly the same! That's where the generics come into play.

```typescript
function myGenericFunction<T>(arg: T): T {
    return arg;
}

```

Well, here's our generic function... and a bit of new syntax too. 😄 With the use of angle brackets (`<>`), just before type arguments declaration section, we declare a  `T`  type (**T**  is the most commonly used name for generic types, generally  **single letters**  are preferred over long names). Then we indicate that the argument and return type are the same, but using this  `T`  type. And that's really generic 😁 because the same variable type is used in multiple places.

But what's the  `T`  type? Is it  `string`,  `number`, etc.? Well, it can be any of those. There are two ways of invoking a generic function.

```typescript
myGenericFunction<string>('str');

```

The first method requires you to directly specify the real type in place of  `T`  type. Here we're using  `string`. We indicate this with the similar angle bracket syntax (it's used very often throughout generics). This way, the type of required argument changes to  `string`, as well as the return type. This is clearly a better and more  **type-safe**  solution than  `any`  or even union types.

```typescript
myGenericFunction(10);

```

The second, more commonly used method takes advantage of TS type inference and more specific  **argument type inference**. This is where generics clearly shine. Our  `T`  type, inferred from our  `10`  argument, takes the type of  `number`. This choice can be later seen in all places, where  `T`type was used.

At this point, you should have a pretty good understanding of what generics are. But, with the example above, I know that you can have doubts about their  **usefulness**. Here, take my word as granted - you'll need to use generics sooner or later (if you'll code in TS obviously 😂) and then you'll discover their  **potential**. This is especially true when combined with some complex types, which we'll learn more about later on or type guards which allow you to utilize generics to much greater extent.

Also, remember about positioning the generic type in functions. It should always be  **before round brackets**  (`()`) aka arguments section. The same goes for arrow functions. Even more general answer would be to set them in place where you can later safely put angle brackets when invoking. You'll most likely get used to it.

## Generic world

So, yeah, there are generic functions, but did you know that generics are spread all over TS type system? You can use them pretty much everywhere they fit. Most importantly  **classes**  and  **interfaces**.

```typescript
class MyGenericClass<T, U> {
    myProperty: T;
    myProperty2: U;
    constructor(arg: T) {
        this.myProperty = arg;
    }
}

```

As you can see, classes work really well with generics. Just like in functions, generic type is available anywhere in declared context. Did I mention that you can declare  **more than 1 generic types**? It applies to all places where generics can be used. Simply separate your generic types' names with a comma (`,`), and you're good to go.

```typescript
interface MyGenericInterface<T> {
    myProperty: T;
    myProperty2: T[];
}

```

Above is the example of using generics with interfaces. It's looking just like with classes. Notice that the second property is an array of T type. I just wanted to yet again demonstrate how well all TS type system components  **work together**.

As classes and interfaces are different from functions, you cannot use argument type inference to call them. You're left with the first method - passing the specific types directly. Otherwise, T will be equal to an  **empty object literal**.

```typescript
interface MyGenericInterface<T> {
    myProperty: T
}

class MyGenericClass <U> {
    myProperty: MyGenericInterface<U>;
    
    constructor(arg: U) {
        this.myProperty = {
            myProperty: arg
        }
    }
}
```

This example also showcases how you can nest and make even better use of generics. Notice how we pass class generic type  `U`  to  `MyGenericInterface`  in  `myProperty`.

## Another array

To finalize the generics section, there's yet one more thing. Remember how we used a special syntax to specify array type e.g.  `string[]`. Well, there's yet another method of doing the same thing. You can use built-in generic  **Array interface**  and easily achieve the same result with  `Array<string>`. It's a very common practice. You can see it throughout the official  **TS standard library**  (typings/declaration files for all JS features, Web APIs & more) and also in other popular  **declaration files** (we'll cover them later), such as React's.

# Complex types

With generics, a whole new level of possibilities open for you. Now we can explore types that when combined with generic give you much finer control. With them, you can express pretty interesting structures. Nevertheless, it's time to discover them too! 😎

## Extended types

You already know the  `extends`  keyword that can be used with classes and interfaces. But in TypeScript, it also has its use-case with generics. Here, you can use it to limit/**specify the type**that generic type should  **extend**  from. Let me explain this with an example.

```typescript
function myGenericFunction<T extends string>(arg: T): T {
    return arg;
}

```

Here we directly specify that our generic type should extend string type. Naturally, it would most likely mean that it should be just  `string`. But, when you specify the type as some kind of class, its derivatives will be assignable too. Generally, it allows you to better specify your generic type and what properties it should have, just like  `extends`  with classes and interfaces.

## Conditional types

**Conditional types**  are quite new to TS type system. Introduced in TypeScript  **v2.8**, they let you choose the right type, based on a  **conditional check**. Checks can be performed with well-known to us  `extends`  keyword and simple syntax:

```typescript
type MyType<T> = T extends string ? boolean : number;

```

Above we have type alias (also can be generic) with a conditional type assigned to it. We check if our generic T type extends string type. If it does, we resolve to boolean, and number otherwise. Naturally, you can use this technique with other types, as well as  **nest**  multiple if statements (they're types anyway 😉).

## Index types

### Index signature

We already covered what to do when you want to declare a property in a class, interface or object literal. But what about a situation, where you want to create an object of an  **unspecified number of keys**, each of which having the same type? Naturally, TS has a solution for that! 😯

```typescript
interface MyInterface {
    [key: string]: number;
}

```

This feature is called  **index signature**  and can be used in interfaces, classes and object literals. The syntax consists of square brackets (`[]`), with a general name for property key and its type inside (generally string, optionally number). After that comes the type of property value. You can read it as every property (named key of type string in this example) should have a value of type number.

Remember that TS types can be  **mixed together**, so you can freely use index signature with tricks like an optional indicator or default value. Also, when creating a structure that besides index signature has other properties, keep in mind that they have to be assignable to the declared signature too!

### Keyof

Let's say you've got an object, interface or whatever, and want to create a function that takes your object's property name as an argument and returns its value. Naturally, you could just declare argument type as a string, but you wouldn't get as much IDE support as you would with a  **union of string literals**. And that's where the  `keyof`  operator comes in.

```typescript
const myObject = {
    a: 1,
    b: 2,
    c: 3
}

function getProperty<T extends keyof (typeof myObject)>(propertyName: T): (typeof myObject)[T] {
    return myObject[propertyName];
}

```

Here we've got some complex typing! Take a moment and analyze it yourself. It basically let us specifically type the argument as a union type  `'a'|'b'|'c'`  with the addition of truly specific return type declaration.

### Indexed access

In the previous example, you should have seen the return type using what seems similar to JS  **square bracket notation**  for accessing object properties. And that's pretty much exactly what we do here, but with types!

```typescript
interface MyInterface {
    myStringProperty: string
}

type MyString = MyInterface['myStringProperty'];

```

Here we're accessing the  `myStringProperty`  of  `MyInterface`  and assigning it to  `MyString`  type alias, which in the result is equal to string. Understandable, right? 🚀

## Mapped types

Mapped types as their name suggest allow to map/transform your types into different forms. With them, you can process given type and change it in any possible way you want.

```typescript
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}

```

Here we have a practical example. Our generic  `Readonly`  type takes  `T`  type and transforms it, so every property is now  **read-only**. The syntax resembles the one of index signature, but with a slight difference. Instead of standard property name and it's type pair, we've got an  `in`  keyword. This allows us to  **iterate**  (a reference to  **for... in**  loop) over the union of type  `T`  keys, defining  `P`type (string literal). Generally speaking, we iterate over T type properties and change them to create a new type. Just like the  `.map()`  method of JS array. 😉

# Declaration files

TypeScript being a superset of JavaScript can easily benefit from JS's  **great ecosystem**  and set of libraries. But type inference can't help with everything. In this case, any type is used, which results in inferior type-safety. To deal with this problem TS provides an option to create so-called  **declaration files**  (aka  **typings**). Usually ending with  **.d.ts**  extension, these files provide information to the TS compiler about types in JS code. This allows using JS libraries in TS with high-quality  **type safety**.

A great number of popular JS libraries already provide their own typings either bundled within the  **NPM**  package or separately as a part of  **[DefinitelyTyped](https://definitelytyped.org/)**  repository. But, if there's no declaration files for your library-of-choice you can quickly create your own based on the documentation and other resources about the particular tool.

Creating your own typings is not that much harder than writing TS code, just without the JS part, meaning  **types only**. Also, you'd have to often use the  `declare`  keyword before functions and variables to declare them.  **Official TS documentation**  provides a  [great read](http://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html)  on this topic, so check it out if you're interested.

# Declaration merging

**Declaration merging**  is an important concept in TypeScript that lets you merge multiple declarations of the given structure into one. Here's an example of merging 2 same interface declarations.

```typescript
interface MyInterface {
    myStringProperty: string;
}
interface MyInterface {
    myNumberProperty: number;
}

```

Resulting interface under the name of  `MyInterface`  will have both, separately declared properties. The same practice can be used with some other TS structures like  **classes**  (partially),  **enums**  and  **namespaces**.

## Module augmentation

In cases where you need to augment/change given value across multiple JS modules, to provide the sufficient type-safety you need to use  **module augmentation**. You can achieve it by using the  `declare module`  keywords pair.

```typescript
import MyClass from './classes';

declare module './classes` {
    interface MyClass {
        myBooleanProperty: boolean;
    }
}
MyClass.prototype.myBooleanProperty = true;

```

# That's it?

With this article, we covered pretty much everything needed to create professional TypeScript code. There still are some more features like  **namespaces**  and  **mixins**, but coding for almost 2 years, I don't really find them so needed or even useful for that matter.

With that said, I think it's the end of this TypeScript introduction. Naturally, be sure to read the first two parts if you want. Maybe you'd like to see some more TS stuff on this blog? Maybe something like a full overview of the  **TS configuration file**  or a tutorial on how to utilize the knowledge learned in this series? Let me know  **in the comments**  or with  **your reaction**  below. 👏

As always,  **follow me  [on Twitter](https://twitter.com/areknawo)**  and  **[on my Facebook page](https://www.facebook.com/Areknawos-Blog-770620059945105)**  for more content. Also, consider consider checking out my [**personal blog**](https://areknawo.com). 🚀

# Resources

-   [**TypeScript - A Tour of Generics**](https://www.dotnetcurry.com/typescript/1439/typescript-generics)  from  "dotnetcurry.com";
-   **[Migrating to Typescript: Write a declaration file for a third-party NPM module](https://medium.com/@chris_72272/migrating-to-typescript-write-a-declaration-file-for-a-third-party-npm-module-b1f75808ed2)**  from  "medium.com";
-   [**How to master advanced TypeScript patterns**](https://medium.freecodecamp.org/typescript-curry-ramda-types-f747e99744ab)  from  "medium.freecodecamp.org";