Typescript notes

rubiin

Rubin

Posted on May 7, 2022

Typescript notes
  • unknown type functions like any but does not allow the data method to be called Typescript would throw a compile time error as we are using string function without type assertion. To fix it, the correct code would look like
 function readAny(val: unknown){

    if( typeof val === 'string')
     return val.trim();
}
Enter fullscreen mode Exit fullscreen mode
  • to check null/undefined use, == eg.
if(value == null) // check for both null and undefined
Enter fullscreen mode Exit fullscreen mode
  • intersection types (&) behaves like inheritance in classes meaning the attributes of one type is inherited by another type without causing duplication
  • example:
type Point2d = {
  x: number;
  y:number;
}

type Point3d = Point2d & {
  z:number;
}

const p1: Point2d = {x: 1, y:2}
const p2: Point3d = {x: 1, y:2, z: 9}

Enter fullscreen mode Exit fullscreen mode

point 3D has all the members of point 2D plus member z. All members in intersection types are required. Not passing "z" on Point3D type will cause error

  • Union type is defined by pipe operator "|". It means the type can either be the single types or their combination. All property of combination are optional
interface A {
  a1: string,
  a2: string,
}

interface B {
  b1: string,
  b2: string;
}

type UnionAB = A | B;


const unionAB1: UnionAB = {
  a1: 'xxx',
  a2: 'xxx',
  b1: 'xxx',
  b2: 'xxx',
};

const unionAB2: UnionAB = {
  a1: 'xxx',
  a2: 'xxx',
};

const unionAB3: UnionAB = {
  b1: 'xxx',
  b2: 'xxx',
};

// Error
// Property 'a1' does not exist on type 'B'.
console.log(unionAB3.a1);

const unionAB4: UnionAB = {
  a1: 'xxx',
  a2: 'xxx',
  b2: 'xxx',
};

// Error
// Property 'b1' does not exist on type 'UnionAB'.
// Property 'b1' does not exist on type 'A'.
console.log(unionAB4.b1);


// Error
// Type '{ a1: string; b2: string; }' is not assignable to type 'UnionAB'.
// Property 'b1' is missing in type '{ a1: string; b2: string; }' but required in type 'B'.
const unionAB5: UnionAB = {
  a1: 'xxx',
  b2: 'xxx',
};

Enter fullscreen mode Exit fullscreen mode
  • optional variables in a type are annotated with "?" . This means the values may or may not be passed. works on class variables as well example:
type PointXd = {
  x: number;
  y:number;
  z?:number;
}

const p1: PointXd = {x: 1, y:2}
const p2: PointXd = {x: 1, y:2, z: 5}

console.log(p1)
console.log(p2)
Enter fullscreen mode Exit fullscreen mode
  • Literal Types - This feature permits you to create a set of relationship values.
type Direction = "North" | "South" | "East" | "West";
Enter fullscreen mode Exit fullscreen mode

Literal types in this case create also a Type Guard of your field, so the compiler can detect your errors or your typos

let directionError: Direction = "east" // Type '"east"' is not assignable to type 'Direction'
let direction: Direction = "East" // OK

Enter fullscreen mode Exit fullscreen mode
  • keyof operator helps us to extract the object's properties such as Literal-types
type Person = {
  age: number;
  phone: string;
}

type PersonKeys = keyof Person; // "age" | "phone" 

usage with in operator to set all fields to number

type Student = {
  [key in keyof Person]: number ;
}


const student: Student = {
age: 18,
phone: "1252672"    // Type 'string' is not assignable to type 'number'
}

Enter fullscreen mode Exit fullscreen mode
  • disciminated types
    example

  • Not null assertion can be made using "!" operator.
    example: https://dev.to/this-is-learning/typescript-tips-tricks-non-null-assertion-operator-21eb

  • Interface and type are somewhat same but types offer much feature . & can be replaced with extends for interface

  • Interface supports declaration merging which is same as union in types . if two interfaces with same name exist then their body is merged
    example:

interface Person {
  name: string;
}

interface Person{
  age: number
}

const person: Person =  {name: 'Max', age: 27}; // merged into a single Person interface

console.log(person);

Enter fullscreen mode Exit fullscreen mode
  • see never type
  • when class implements type/interface , it should have all the attributes mentioned in the interface/types . It acts a blueprint example:
type Person = {
  age: number;
  phone: string;
}

  // Error below: Type 'Student' is missing the following properties from type 'Person': age, phone

class Student implements Person{   
  id: string
}



Enter fullscreen mode Exit fullscreen mode
  • definitive assignment(!) is used to tell typescript that the value will be defined and it is upto you to make sure its defined.

let person: string


 function test(){
  person = "rubin"
 }

 test()

 console.log(person)  // error TS2454: Variable 'person' is used before being assigned.

Even though person is initialized inside the test function, typescript doesnot know this. 
To tell typescript that this value will always be non null,  add "!" and the error goes away

let person!: string



Enter fullscreen mode Exit fullscreen mode
  • Type guards are used with "is" keyword and used to assert if a value is of certain type.
type Rectangle ={
    length: number,
    breadth: number
}


type Square ={
  size: number
}

type Shape = Rectangle | Square;

function isRectangle(shape: Shape): shape is Rectangle {
  return "length" in shape && "breadth" in shape;
}

function isSquare(shape: Shape): shape is Square{
  return "size" in shape;
}


function printArea(shape: Shape) {
    if (isRectangle(shape)) {
        console.log(shape.length * shape.breadth);
    }
    if(isSquare(shape)){
      console.log(shape.size * shape.size);
    }


}

Here we are telling typescript if the function returns true, 
the value is of type "Person"


Notice: the guard can also be defined as 
function isSquare(shape: Shape): boolean{
  return "size" in shape;
}

However typescript wont know the shape is "Square" even if it returns true
so its best to use the above for type checking



Enter fullscreen mode Exit fullscreen mode
  • An abstract class is typically used to define common behaviors for derived classes to extend. Unlike a regular class, an abstract class cannot be instantiated directly. The unimplemented methods are to be implemented and defined by the classes that extends it
abstract class Logger{
 abstract prefix(): string

log(message: string){
    console.log(this.prefix() +":"+ message)
}
}

class ConsoleLogger extends Logger{
  prefix(): string {
      return "Console"
  }

}

const logger = new ConsoleLogger()
logger.log("Hello")

Enter fullscreen mode Exit fullscreen mode
  • Object in js can be accessed with index-signature i.e Obj[key] similar to array . In typescript we can define the type of the index while accessing the object values
type Person= {
  userName: string;
  address: string;
}

const p1: Person = {
  userName: 'alex123',
  address: '123 Main St.',
}
const p2: Person = {
  userName: 'ryan123',
  address: '123 Main St.',
}


type PersonDictionary= {
  [key: string]: Person
}

const persons: PersonDictionary = {
  alex: p1,
  ryan: p2
}

persons['alex'].userName = 'alex567' 

console.log(persons['alex'])

Enter fullscreen mode Exit fullscreen mode
  • Tuples are arrays with fixed length

  • Generic constraints are used to require generic parameters to have a particular structure. If you don't specify them and try to access the data's property , typescript will throw error as it does not know the dats structure

const addFullName = <T>(obj: T) => {
  return {...obj, fullName: `${obj.firstName} ${obj.lastName}`};
}
Enter fullscreen mode Exit fullscreen mode

Here we have defined a parameter of type generic T. If we try to access, the properties of the parameter,
even if it exists, we get error like Property 'lastName' does not exist on type 'T'. We can fix this by adding generic constraints which tells typescript that the generic has those properties. Additionally, its also requiring the passed value to have properties firstName and lastName

type NamedVales = {firstName: string, lastName: string};

const addFullName = <T extends NamedVales>(obj: T) => {
  return {...obj, fullName: `${obj.firstName} ${obj.lastName}`};
}
Enter fullscreen mode Exit fullscreen mode
  • The typeof key can be used to extract type from an existing data
const user = {
  name: "Rubin",
  age: 15,
  address: "Kathmandu"
}

type UserType = typeof user  //  {name: string,age:number,address: string}
Enter fullscreen mode Exit fullscreen mode

-Look up types are used to extract a portion from a complex type and create a new type

type requestType = {

  payload: {
    name: string,
    user: number,
    roles: {
      edit: boolean,
      create: boolean,
      read: boolean
    }
  },
   params: {
     id: number,
     type: string
   }
}

// if we want to use type of  params as a type then

type Params = requestType["params"]  //  {id: number,type: string }
type Roles = requestType["payload"]["roles"] //   roles: {edit: boolean,create: boolean,read: boolean}

Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
rubiin
Rubin

Posted on May 7, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related