Sujay Kundu

Typescript – Using Types

Types in Typescript :

  • number ( 1, 5.3, -10)
  • string (“H1”, ‘Hi’)
  • boolean (true, false)
  • object {age:30}
  • Array [1,2,3] – Any Javascript Array
  • Tuple. [1,2] (Fixed-length and Fixed type array)
  • Enum enum { NEW, OLD } – Automatically enumerated global constant identifiers
  • Any * (Any kind of value, no specific type, flexible)
  • Union (combining types)
  • Literal
  • Function
  • unknown
  • never

The Key difference is : Javascript uses “dynamic types” (resolved at runtime), Typescript uses “static types” (set during development)

Type casting:

In Typescript, you work with types like string or number all the times.

Important: It is string and number (etc.), NOT String, Number etc.

The core primitive types in Typescript are all lowercase!

Let’s consider the example of our (Sum.js) which adds two numbers


// In Javascript
function add (n1, n2) {
  return n1 + n2;
}

const number1 = '5';
const number2 = 2.8;

const result = add(number1, number2);
console.log('result', result); // 52.8. (Since n1 is concatenating with n2)

Now this, can be resolved with Typescript

// In Typescript
function add (n1: number, n2: number) {
  return n1 + n2;
}

const number1 = 5; 
const number2 = 2.8;

const result = add(number1, number2);
console.log('result', result); // 7.8. 

Objects

Let’s create a object

// in Javascript
const person = {
  name: 'Sujay',
  age: 28,
}
console.log(person.name); 
console.log(person.age);

Typescript uses Type Inference

// in Typescript
const person: object = {
  name: 'Sujay',
  age: 28,
}
console.log(person.name); // This will throw error in Typescript since it infers the types of the object

// we can add types for the object like this
const person: {
  name: string;
  age: number;
} = {
  name: 'Sujay',
  age: 28,
};

console.log(person.name); // This will be fine.

Nested Objects & Types

Of course object types can also be created for nested objects.

Let’s say you have this Javascript Object:

const product = {
  id: 'abc',
  price: 12.99,
  tags: ['great-offer', 'hot-and-new'],
  details: {
    title: 'Red Carpet',
    description: 'A great carpet - almost brand-new!'
  }
}

This would be the type of such an object:

{
  id: string;
  price: number;
  tags: string[];
  details: {
    title: string;
    description: string;
  }
}

So you have an object type in an object type so to say.

Array Types

const person = {
  name: 'Sujay',
  age: 30,
  hobbies: ['Sports', 'Cooking']
};

let favouriteActivites: string[]; // array of strings
favouriteActivites = 'Sports' // this will throw error since its not an array.

favouriteActivites = ['Sports'];

for (const hobby of person.hobbies) {
  console.log(hobby.toUpperCase());
}

Tuple Types

When you know exactly x amount of values in a array and you know the exact types of each value. then Tuple can be useful.

const person = {
  name: string;
  age: number;
  hobbies: string[],
  role: [number, string]; // defining the type of tuple which will be fixed
} = {
  name: 'Sujay',
  age: 28,
  hobbies: ['Sports', 'Cooking'],
  role: [2, 'author']
}

console.log(person.role);

Enums

Defining custom enum, whenever you need identifiers that are human readable

enum Role { ADMIN = 'ADMIN', READ_ONLY = 200, AUTHOR = 'AUTHOR' };

const person = {
  name: 'Sujay',
  age: 30,
  hobbies: ['Sports', 'Cooking'],
  role: Role.ADMIN
}

Any

any is a flexible type to tell typescript, that the type to infer can be anything. Usually we wanna avoid this as much as possible

const value: any = '334';

Union

It is used to combine the types, using | (pipe) operator we can declare multiple types

// typescript

// using union type to decribe the type of input1 as it can be number or string using |
function combine(input1: number | string, input2: number | string) {
  let result;
  if (typeof input1 === 'number' && typeof input2 === 'number') {
    result = input1 + input2;
  } else {
    result = input1.toString() + input2.toString();
  }
  return result;
}

const combinedAges = combine(30, 23);
console.log(combinedAges) // 53

const combinedNames = combine('Max', 'Anna');
console.log(combinedNames); // MaxAnna

Literal Types

Literal Types in Typescript are types that constraint the type of a variable to a specific value.

 For example, the type "hello world" represents the string value "hello world".

Literal types can be used to specify the exact value for a variable or property, providing precise and self-documenting code.

Here is an example of how literal types can be used to specify the exact value for a variable:

const greeting: string = "hello world";

In this example, the variable greeting is assigned the string value "hello world". The literal type "hello world" ensures that the variable greeting can only be assigned the string value "hello world".

Literal types can also be used to specify the exact value for a property.

Here is an example of how literal types can be used to specify the exact value for a property:

interface Person {
  name: string;
  age: number;
}

In this example, the interface Person defines a property name of type string and a property age of type number. The literal types "John" and 30 ensure that the properties name and age can only be assigned the string value "John" and the number value 30, respectively.

Literal types are a powerful tool that can be used to improve the precision and self-documentation of your code.

// typescript (index.ts)
function combine (
  input1: number | string,
  input2: number | string,
  resultConversion: 'as-number' | 'as-text', //literal types 
) {
  let result;
  if (typeof input1 === 'number' && typeof input2 === 'number' || resultConversion === 'as-number') {
    result = +input1 + +input2; // converting to numbers before adding
  } else {
  result = input1.toString() + input2.toString();
  }
  return result;
}

const combinedAges = combine(30, 26, 'as-number');
console.log('combinedAges', combinedAges);

const combinedStringAges = combine('30', '26', 'as-number');
console.log('combinedStringAges', combinedStringAges);

const combinedNames = combine('Max', 'Anna', 'as-text');
console.log('combinedNames', combinedNames);

Type Alias / Custom Types

We can create a new type alias

// typescript (index.ts)
type Combinable = number | string; // custom types
type ConversionDescriptor = 'as-number' | 'as-text'; //literal types 


function combine (
  input1: Combinable,
  input2: Combinable,
  resultConversion: ConversionDescriptor, 
) {
  let result;
  if (typeof input1 === 'number' && typeof input2 === 'number' || resultConversion === 'as-number') {
    result = +input1 + +input2; // converting to numbers before adding
  } else {
  result = input1.toString() + input2.toString();
  }
  return result;
}

const combinedAges = combine(30, 26, 'as-number');
console.log('combinedAges', combinedAges);

const combinedStringAges = combine('30', '26', 'as-number');
console.log('combinedStringAges', combinedStringAges);

const combinedNames = combine('Max', 'Anna', 'as-text');
console.log('combinedNames', combinedNames);

Type Aliases & Object Types

Type aliases can be used to “create” your own types. You’re not limited to storing union types though – You can also provide an alias to a (possibly complex) object type.

For example:

type User = { name: string; age: number };

const u1: User = {
  name: 'Max', age: 30
};

This allows you to avoid unnecessary repetition and manage types centrally.

For example, you can simplify this code :

function greet(user: { name: string; age: number }) {
  console.log('Hi, I am ' + user.name);
}

function isOlder(user: { name: string; age: number }, checkAge: number) {
  return checkAge > user.age;
}

To:

type User = { name: string; age: number };

function greet(user: User) {
  console.log('Hi, I am' + user.name);
}

function isOlder(user: User, checkAge: number) {
  return checkAge > user.age;
}

Function Return Types & “void”

function add (n1: number, n2: number) {
  return n1 + n2;
}

// we can add a return type
function add (n1: number, n2:number) : number {
  return n1 + n2;
}

// void return type
function printSum(num: number) : void {
  console.log('Result' + num); // does not return anything
}

printSum(add(8,8));

We can’t use undefined

// we can add a return type
function add (n1: number, n2:number) : number {
  return n1 + n2;
}
// void return type
// void return type
function printSum(num: number) : void {
  console.log('Result' + num); // does not return anything
}

function printSum(num: number) : undefined {
  console.log('Result' + num); 
  return; // this won't throw any error, but will throw error if you remove the return
}

Function as Types

Function types defines the parameters and return type of a function.

// we can add a return type
function add (n1: number, n2:number) : number {
  return n1 + n2;
}

// void return type
function printSum(num: number) : void {
  console.log('Result' + num); // does not return anything
}

printResult(add(5,12));

let combineValues: (a: number, b: number) => number;  // Function Type parameters with return type

combineValues = add; // this is fine
combineValues = printResult; // this throws error, since our arguments doen't match the types

console.log('combineValues', combineValues(8, 8));

Function Types & Callbacks

// we can add a return type
function add (n1: number, n2:number) : number {
  return n1 + n2;
}

function printResult(num: number) : void {
  console.log('Result: ' + num);
}

function addAndHandle(n1: number, n2: number, cb: (num: number) => void) { // function type def
  const result = n1 + n2;
  cb(result); //pass it to callback function
}

printResult(add(5, 12));

addAndHandle(10, 20, (result) => {
  console.log('callback result', result);
});

Which code snippet is better (i.e. which code should you write) ?

1. function sayHi(): void {
    //...
    }
OR
2. function sayHi(): undefined {
   // ...
}

=> 1. because it doesn't force you to return anything if you don't want to return something.

Will this code compile ?

function sendRequest(data: string, cb: (response: any) => void) {
  // ...sending a request with "data"
  return cb({data: 'Hi there!'});
}

sendRequest('Send this!', (response) => {
  console.log(response);
  return true;
});

Yes! As callback functions can return something, even if the argument on which they’re passed does NOT expect a returned value.

unknown

unknown is a better choice than any, need to do a extra type checking

let userInput: unknown;
let userName: string;

userInput = 5;
userInput = 'Max';
if (typeof userInput==='string') {
  userName = userInput
}

never

never is used when we know the function that never returns anything.

function generateError(message: string, code: number): never { // return type is never since it never returns anything 
  throw { message: message, errorCode: error };
}

const result = generateError('An error occurred!', 500);
console.log('result', result); // never returns not even undefined