Master typescript types in 7 minutes.
Tags: typescript, javascript, programming, frontend
Published: 2023-08-05 19:00:09

# Types in C# and Java
You may be familiar with the types in managed-type languages like Java or C#. In these languages, we have two kinds of types.
### primitive types
> - These types are value-based 
> - These types are predefined by the language
> - Examples are `int`, `string`, `float`, `bool`, `byte`, `double`, 
> - In c#, you can use `typeof` operator to get the type of a type and `GetType()` method to get the type of a variable.

```c#
Console.WriteLine(typeof(int)); // System.Int32
Console.WriteLine(typeof(byte)); // System.Byte
Console.WriteLine(typeof(double)); // System.Double
Console.WriteLine(typeof(string)); // System.String
var str = "string";
Console.WriteLine(str.GetType()); // System.String
```
> - In java, you can use `instanceof` operator to check if an object is a type or not

```Java
Object d = 0.0;
System.out.println(d instanceof Double);//true
```
### Non-primitive types
> - These types are generally referenced based (except Enums and struct in c#)
> - These types are user-defined types and users can define and create them using class, enum, struct, interface, delegate, etc.
> - In c# you can use `typeof` operator to get the type of a Type and `GetType()` method to get the type of a variable.

```C#
class Program {
    public static void Main() {
       Console.WriteLine(typeof(Program));//Program
       var program = new Program();
       Console.WriteLine(program.GetType());//Program
   }
}

```
> - In Java, you can use `instanceof` operator.

```java
class Car{
   interface IExample {
         public void print();
   }
   public static void main(String args[]){
     Car c=new Car();
     System.out.println(c instanceof Car);//true
        
     Object obj = new IExample() {
            public void print() {

            }
        };
     System.out.println(obj instanceof IExample);//true
   }
}
```

---

# Types in typescript
### primitive types
> - Typescript's primitive types are `string`, `number`, `boolean`, `bigint`, `undefined`
> - These types are value-based 
> - These types are predefined by the language
> - You can use `typeof` operator to get the types of values.

```typescript
    let big1 = 9007199254740991n;
    console.log(typeof(big1)) // "bigint"

    let bool = true
    console.log(typeof bool) // "boolean"

    let undef
    console.log(typeof undef)//"undefined"
```
> - You can consider primitive types in Typescript to work similarly to those in Java and C#, but actually they do not! Javascript types have more to offer. You will see in a moment.

### Non-primitive types
> - These types are generally referenced based (except Enums)
> - These types are user-defined types and users can define and create them using class, type, interface, function, etc.
> - Do not confuse the general word type which is a programming concept with Typescript's `type` keyword which is one way of creating types.
> - You can use `typeof` operator to get the types of values.

```typescript
    type Obj = { x: string }
    const obj: Obj = { x: "" }
    console.log(typeof obj) // "object"

    class Class {  constructor(public a: number){} }
    const cls: Class = new Class(1)
    console.log(typeof cls)// "object"

    interface Interface { i: number }
    const obj2: Interface = {  i: 1 }
    console.log(typeof obj2)// "object"

    let nl = null
    console.log(typeof nl)// "object"
```
> - As you may have noticed, when it comes to the `typeof` operator in TypeScript, the results for non-primitive types are all "object". Here, it's important to adjust our understanding of types in TypeScript. In Typescript Instead of thinking of a type as a rigid definition, consider it as a set (Unique values) that encompasses multiple values. This characteristic makes TypeScript types incredibly flexible and powerful. In languages like C# and Java, a variable can only have a single type, and you can create new types by utilizing techniques like inheritance or interface implementation. However, in TypeScript, you not only have access to those same techniques, but you also have a wide range of additional methods to manipulate types like combining them, filtering a type to create a new type, map an existing type to another one and so on. Below I introduce the patterns that you can use to work with types in Typescript and see how they are different from managed-type languages.

#### Unions of primitive types / values
In languages like C# and java, a variable can only be one type at the same time. For example either a string or number and so on. In Typescript you can use the union of multiple types as a new type or union of multiple values as a new type.

```typescript
    //union of two primitive types
    type stringOrNumber = string | number;
    let son: stringOrNumber = "text"
    console.log(typeof son) // "string"
    console.log(son) // "text"
    son = 1
    console.log(typeof son) //number
    console.log(son) // 1

    //union of multiple number values
    type DiceNumber = 1 | 2 | 3 | 4 | 5 | 6;
    let diceNumber: DiceNumber = 1
    //let diceNumber: DiceNumber = 10 // error: Type '10' is not assignable to type 'DiceNumber'
    console.log(typeof diceNumber) // "number"
    console.log(diceNumber) // 1

    //template union types
    type Langs = 'en' | 'fr'
    type texts = 'footer' | 'header'
    type combine = `${Langs}_${texts}`
    /*
        type combine = "en_footer" | "en_header" | "fr_footer" | "fr_header"
    */

```
 
#### Extend `object` types
You can extend type, class or interface types to create new types. For classes and interfaces, aside from minor differences, it is what you are also doing in languages like C# or Java.

```typescript
    type Person = { name: string; age: number;};
    //Create a new type by removing the name
    type PersonWithoutName = Omit<Person,'name'>
    //create new type by adding id to Person type
    type PersonWithId = {id: string; } & Person;
    //create a new type using intersection of two other types
    type Dim1 = {x: number}
    type Dim2 = {y: number, z: number}
    type Dim3 = Dim1 &  Dim2

    interface IPersion {name: string}
    //create new type by extending IPersion
    interface IPersopnWithId extends IPersion { id: number;}
    // Add properties to an existing interface by redeclaring the interface name. (You cannot do this for `type`)
    interface IPersion {age: number}

    class Parent {
        constructor(public a: number) {}
    }
    // Create a new type by extending Parent class
    class Child extends Parent {
        constructor(public b: number, public a: number)
        {
            super(a);
        }
    } 
```

#### Map an existing type to another by applying a `mapper type`
You can create a new type from an existing type by changing the properties of the old one using a mapper.

```typescript
    type Artist = {
        id: number;
        name: string;
        bio: string;
    }
    // a mapper that makes all fileds of the old type optional and add a mandatory id filed
    type MyPartialTypeForEdit<Type> = {
    [Property in keyof Type]?: Type[Property];
    } & { id: number };
    // this type contains all fileds from Artist type but optional
    type ArtistOptionalFields = MyPartialTypeForEdit<Artist>;
        /*
        type ArtistOptionalFields = {
            id?: number | undefined;
            name?: string | undefined;
            bio?: string | undefined;
        } & {
            id: number;
        } 
        */
```
#### Filter types by applying a `conditional type`
You can create a new type by setting some rules. Then apply this conditional type to a compound type to filter it.

```typescript
    // If A extends this type-> {legs: 4}, then A has four legs, otherwise never.
    type HasFourLegs<A> = A extends {legs: 4} ? A : never
    type Bird = { legs: 2, wings: number };
    type Dog = { legs: 4, breed: string};
    type Ant = { legs: 12};
    type Lion = { legs: 4};
    type Animals = Bird | Dog | Ant | Lion
    type four_legs = HasFourLegs<Animals> // Dog | Lion
```

#### Mix classes using `mixins`
In typescript, like most other programming languages, a class can only inherit from one parent class. But what if we want to have the capabilities of multiple classes in one class (just like C++). Do not worry. Typescript allows you create a new type by mixing classes.

```typescript
    class Jumpable { jump() { console.log("jump");} }
    class Duckable { duck() {console.log("duck");} }
    //Our mixed class
    class Sprite { x = 0; y = 0; }
    // Then you create an interface which merges
    // the expected mixins with the same name as your base
    interface Sprite extends Jumpable, Duckable {}
    // Apply the mixins into the base class via the JS at runtime
    applyMixins(Sprite, [Jumpable, Duckable]);
    
    let player = new Sprite();
    player.jump();
    player.duck();
    
    // This can live anywhere in your codebase:
    function applyMixins(derivedCtor: any, constructors: any[]) {
        constructors.forEach((baseCtor) => {
            Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
            Object.defineProperty(
                derivedCtor.prototype,
                name,
                Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
                Object.create(null)
            );
            });
        });
    }
```

#### Picking properties from another type
You can use `Pick<>` to pick certain properties from another type and create a new type.

```typescript
Type User = {
    id: string,
    name: string,
    age: string,
    email: string,
    phone: number,
    password: string
}

Type LoginUser = Pick<User,'email' | 'password'>

```

What else you can add to this list?