Category: 07. Type Manipulation

https://cdn3d.iconscout.com/3d/premium/thumb/3d-object-manipulation-in-vr-3d-icon-png-download-13101740.png

  • Template Literal Types

    Template string literals are used to create dynamic strings with variables in JavaScript. Similarly, in TypeScript you can use template literal types to create the dynamic types, which is introduced in the TypeScript 4.1 version. The syntax of the template literal types in TypeScript is very similar to the JavaScript template string literals.

    Syntax

    You can follow the syntax below to use the template literal types in TypeScript.

    typetype_name=some_text ${variable_name}
    • In the above syntax, ‘type_name’ is the name of the type.
    • You need to use the backticks (“) to define template literal types.
    • The ‘some_text’ can be any string value that will remain constant.
    • To add any dynamic value in the type, you need to add the variable name inside the ‘${}’ structure.

    Examples

    Let’s understand the Template Literal Types in details with the help of some examples in TypeScript.

    Example: Basic Use

    In the code below, we have defined the ‘World’ type which contains the World as a value. The ‘Greeting’ type is a template literal type whose value changes based on the value of the ‘World’ variable.

    Next, we created the ‘greeting’ variable of type ‘Greeting’ which contains a Hello World value. If we try to assign another value to it, the TypeScript compiler throws an error.

    typeWorld="world";// Defining a template literal typetype Greeting =Hello, ${World};const greeting: Greeting ="Hello, world";// Correct// const wrongGreeting: Greeting = "Hello, everybody"; // Error: This type is "Hello, world" specifically.console.log(greeting);

    On compiling, it will generate the following JavaScript code:

    const greeting ="Hello, world";// Correct// const wrongGreeting: Greeting = "Hello, everybody"; // Error: This type is "Hello, world" specifically.console.log(greeting);

    The above example code will produce the following output:

    Hello, world
    

    Example: Combining with Union Types

    In this code, the ‘size’ type contains the union of multiple type values. The ‘ResponseMessage’ type is a template literal type whose value changes based on the value of the ‘Size’ type.

    The selectSize() function takes a string of type ‘Size’ as a parameter, and returns the value of type ‘ResponseMessage’.

    Next, we call the function by passing ‘medium’ as a function parameter. If we try to pass any other string value than the ‘small’, ‘medium’, and ‘large’ as a function parameter, it will throw an error.

    // Creating a type using literal typetypeSize="small"|"medium"|"large";// Custom template literal typetype ResponseMessage =${Size} size selected;// Function to select sizefunctionselectSize(size: Size): ResponseMessage {return${size} size selected;}// Call the functionconst response: ResponseMessage =selectSize("medium");// "medium size selected"console.log(response);

    On compiling, it will generate the following JavaScript code:

    // Function to select sizefunctionselectSize(size){return${size} size selected;}// Call the functionconst response =selectSize("medium");// "medium size selected"console.log(response);

    Its output is as follows:

    medium size selected
    

    Example: Conditional String Patterns

    In this example, we have used the generic types with constraints. Here, ‘T extends Status’ means, the value of T can be only from the status. The statusMessage type is a combination of the type ‘T’ and ‘status’ strings.

    The printStatus() function takes the type T as a parameter, which can be any one value from the ‘Status’ type, and returns the value of type ‘statusMessage’.

    Next, we have called the printStatus() function by passing the loading value which is a part of the ‘Status’ type.

    // Definition of the Status typetypeStatus="loading"|"success"|"error";// T can be any of the values in the Status typetype StatusMessage<TextendsStatus>=${T}Status;// Function to return a message based on the statusfunctionprintStatus<T extends Status>(status:T): StatusMessage<T>{return${status} Statusas StatusMessage<T>;}// Call the function with the "loading" statusconst loadingMessage =printStatus("Loading");// "loadingStatus"console.log(loadingMessage);

    On compiling, it will generate the following JavaScript code:

    // Function to return a message based on the statusfunctionprintStatus(status){return${status} Status;}// Call the function with the "loading" statusconst loadingMessage =printStatus("Loading");// "loadingStatus"console.log(loadingMessage);

    Its output is as follows:

    Loading Status
    

    Example: API Route Generation

    The below example demonstrates the real-time practical use of the template literal types.

    Then, We have defined the Method type which can have any value from the REST API method type. The Entity type defines the entity objects.

    The getRoute() method takes the entity and method of type Entity and Method, respectively as a parameter. It returns the string value of type APIRoute after combining the entity and method values.

    // Defining the Method and Entity typestypeMethod="get"|"post"|"delete";typeEntity="user"|"post";// Defining the ApiRoute typetype ApiRoute =/${Entity}/${Method};// Defining the getRoute functionfunctiongetRoute(entity: Entity, method: Method): ApiRoute {return/${entity}/${method}as ApiRoute;}// Using the getRoute functionconst userRoute =getRoute("user","post");// "/user/post"console.log(userRoute);

    On compiling, it will generate the following JavaScript code:

    // Defining the getRoute functionfunctiongetRoute(entity, method){return/${entity}/${method};}// Using the getRoute functionconst userRoute =getRoute("user","post");// "/user/post"console.log(userRoute);

    Its output is as follows:

    /user/post
    

    In TypeScript, the template literal type is mainly used when you need to create dynamic types.

  • Mapped Types

    Mapped types in TypeScript are used to create new types by transforming the properties of existing types. Mapping means creating new elements from the existing ones after making some changes. Similarly, mapped type means creating new types after making changes to the existing types. So, you can reuse the existing types to create new types.

    Built-in Mapped Types

    The built-in mapped type allows you to transform the existing types without manually transforming types.

    Here are the built-in mapped types available in TypeScript.

    • Partial<T>: It creates a new type by making all properties optional of the existing type.
    • Required<T>: It creates a new type by making all properties required of the existing type.
    • Readonly<T>: Makes all properties optional in a new type.
    • Record<K, T>: Creates a type with a set of properties K of type T. Useful for mapping properties of the same type.
    • Pick<T, K>: Creates a new type by picking a set of properties K from T.
    • Omit<T, K>: Creates a new type by omitting a set of properties K from T.
    • Exclude<T, U>: It creates a new type by excluding all properties from T that are assignable to U.
    • Extract<T, U>: It creates a new type by extracting all properties from T that are assignable to U.
    • NonNullable<T>: Constructs a type by excluding null and undefined from T.

    Examples

    Let’s understand some of the commonly used built-in mapped types with help of some program examples in TypeScript.

    Example: Using the Partial Type

    In the code below, we have created the Person type containing the name and age properties. After that, we used the Partial utility type to create a new type from the Person type.

    The PartialPerson type has all the properties of the Person class but all are optional. We have defined the partialPerson object of the PartialPerson type, which contains only the name property as all properties are optional.

    // Creating the Person typetypePerson={
    
    name:string;
    age:number;};// Using the Partial mapped typetype PartialPerson = Partial&lt;Person&gt;;// Creating an object of type PartialPersonconst partialPerson: PartialPerson ={
    name:"John",//   Age is optional};// Outputconsole.log(partialPerson);</code></pre>

    On compiling, it will generate the following JavaScript code.

    // Creating an object of type PartialPersonvar partialPerson ={
    
    name:"John"};// Outputconsole.log(partialPerson);</code></pre>

    Output

    { name: 'John' }
    

    Example: Using the ReadOnly Type

    In the code below, we used the Readonly utility type to create a new type having all properties readonly from the Person type. If we try to change any property of the partialPerson object, it will throw an error as it is readonly.

    // Creating the Person typetypePerson={
    
    name:string;
    age:number;};// Using the ReadOnly mapped typetype PartialPerson = Readonly&lt;Person&gt;;// Creating an object of type PartialPersonconst partialPerson: PartialPerson ={
    name:"John",
    age:30};// Trying to change the name property// partialPerson.name = "Doe"; // Error: Cannot assign to 'name' because it is a read-only property.// Outputconsole.log(partialPerson);</code></pre>

    On compiling, it will generate the following JavaScript code.

    // Creating an object of type PartialPersonconst partialPerson ={
    
    name:"John",
    age:30};// Trying to change the name property// partialPerson.name = "Doe"; // Error: Cannot assign to 'name' because it is a read-only property.// Outputconsole.log(partialPerson);</code></pre>

    Output

    { name: 'John', age: 30 }
    

    Example: Using the Pick Type

    In the code below, we have created the Animal type which contains 3 properties.

    After that, we used the Pick utility type to pick only name and species properties from the Animal type and create a new type named 'AnimalNameAndSpecies'.

    The animalNameAndSpecies object is of type AnimalNameAndSpecies, which contains only name and species properties.

    // Creating the Animal typetypeAnimal={
    
    name:string;
    species:string;
    age:number;};// Using the Pick utility type to create a new typetypeAnimalNameAndSpecies= Pick&lt;Animal,'name'|'species'&gt;;// Creating an object of the AnimalNameAndSpecies typeconst animalNameAndSpecies: AnimalNameAndSpecies ={
    name:'Milo',
    species:'Dog'};console.log(animalNameAndSpecies);</code></pre>

    On compiling, it will generate the following JavaScript code.

    // Creating an object of the AnimalNameAndSpecies typeconst animalNameAndSpecies ={
    
    name:'Milo',
    species:'Dog'};console.log(animalNameAndSpecies);</code></pre>

    Output

    { name: 'Milo', species: 'Dog' }
    

    Creating Custom Mapped Types

    Utility functions have limited functionalities. So, it is important to create the custom mapped types to map types according to your own conventions.

    Syntax

    You can follow the syntax below to create a custom-mapped type.

    typeMyMappedType<T>={[PinkeyofT]: NewType;};
    • 'T' is an existing type from which we need to create a new custom-mapped type.
    • 'P' is a property of the type.
    • The 'keyof' operator gets each key of the type 'T'.
    • NewType is a new type to assign to the type property 'P'.

    Example: Making all Properties Boolean

    In the code below, we created the Booleanify type which takes the type as a generic parameter. Inside the type body, we traverse each key of the type 'T' using the '[P in keyof T]' code, and assign a boolean to it as we are changing the type of all properties to a boolean.

    The boolPerson object has all properties of the boolean type.

    // Defining the Person typetypePerson={
      name:string;
      age:number;};// Creating the custom mapped typetype Booleanify<T>={// Making all properties of Person type to Boolean[PinkeyofT]:boolean;};// Creating an object of Booleanify typeconst boolPerson: Booleanify<Person>={
      name:true,
      age:true,};console.log(boolPerson);

    On compiling, it will generate the following JavaScript code:

    // Creating an object of Booleanify typeconst boolPerson ={
    
    name:true,
    age:true,};console.log(boolPerson);</code></pre>

    Output

    The above example code will produce the following output:

    { name: true, age: true }
    

    Example: Making all Properties Optional

    In this example, we are making all properties of existing types optional without using the built-in utility type Partial.

    Here, the Animal class contains the name and legs properties. In the code [key in keyof Animal]?: Animal[key], question mark (?) makes properties optional, and Animal[key] helps in keeping the property type same.

    Now, In the customAnimal object, all properties are optional.

    // Defining the Animal typetypeAnimal={
    
    name:string;
    legs:number;};// Creating custom type and making all properties optional using mappingtypeCustomAnimal={&#91;key inkeyof Animal]?: Animal&#91;key];};// Creating an object of type CustomAnimallet customAnimal: CustomAnimal ={
    name:"Dog",
    legs:4,};console.log(customAnimal);</code></pre>

    On compiling, it will generate the following JavaScript code:

    // Creating an object of type CustomAnimallet customAnimal ={
    
    name:"Dog",
    legs:4,};console.log(customAnimal);</code></pre>

    Output

    The output of the above example code is as follows:

    { name: 'Dog', legs: 4 }
    

    You can either use the utility functions or create custom mapped types to reuse the existing types to create new types. These custom mapped types improve the readability of the code and help developers to make code maintainable.

  • Conditional Types

    In TypeScript, conditional types allow you to assign a type to the variables based on the conditions. This enables you to define types that dynamically change based on certain conditions. This feature is very useful for large-scale applications, where you need dynamic typing according to the different situations.

    Basic Conditional Types

    We will use the ternary (?:) operator to use the conditional types. It will evaluate the condition, and based on the true or false result, it will select the new type.

    Syntax

    You can follow the syntax below to use the conditional types.

    typeA= Type extendsanotherType? TrueType : FalseType;
    • In the above syntax, the ‘Type extends anotherType’ condition will be evaluated first.
    • If the condition is true, ‘type A’ will contain the ‘TrueType’. Otherwise, it will contain the ‘FalseType’.
    • Here, ‘extends’ keywords check whether ‘Type’ is the same as ‘anotherType’ or at least contains all properties of the ‘anotherType’ type.

    Example

    In the code below, we have defined the ‘car’ type which contains the name, model, and year properties. We have also defined the ‘Name’ type which contains only ‘name’ properties.

    The ‘carNameType’ type variable stores either a string or any value based on the evaluation result of the ‘Car extends Name’ condition. Here, the condition will be evaluated as true as the ‘Car’ type contains all properties of the ‘Name’ type.

    After that, we have created the ‘carName’ variable of ‘carNameType’ and printed it in the console.

    typeCar={
    
    name:string,
    model:string,
    year:number,}typeName={ name:string}// If Car extends Name, then carNameType is string, otherwise it is anytypecarNameType= Car extendsName?string:any;// string// Define a variable carName of type carNameTypeconst carName: carNameType ='Ford';console.log(carName);// Ford</code></pre>

    On compiling, it will generate the following JavaScript code.

    // Define a variable carName of type carNameTypeconst carName ='Ford';console.log(carName);// Ford 

    Output

    Ford
    

    Generic Conditional Types

    Now, let's learn the generic conditional types. In TypeScript, generic types are similar to the parameters in the function. It allows developers to define a conditional type such that it can be used at multiple places in the code. It provides flexibility to use the different types in the conditional statements.

    Syntax

    You can follow the syntax below to use the generic conditional types.

    typeConditionalType<T>=TextendsType1? TrueType : FalseType;
    • In the above syntax, 'conditionalType<T>' has a type 'T' parameter, and 'conditionalType' is the name of the type.
    • The condition 'T extends Type1' checks whether type 'T' extends 'Type1'.
    • If the condition evaluates true, 'TrueType' will be assigned to the 'ConditionalType'. Otherwise, 'FalseType' will be assigned.

    Example

    In the code below, 'IsNumArray<T>' is a generic type that takes type 'T' as a parameter. It checks whether the type of the 'T' is an array of numbers (number[]). If yes, it returns 'number'. Otherwise, it returns 'string'.

    After that, we defined the 'num' and 'str' variables and used the 'IsNumArray' type with them. For the 'num' variable, we have used the 'number[]' parameter, and for the 'str' variable, we have used the 'string[]' parameter.

    // Generic conditional typestypeIsNumArray<T>=Textendsnumber[]?number:string;const num: IsNumArray<number[]>=5;// numberconst str: IsNumArray<string[]>='5';// stringconsole.log(num);console.log(str);

    On compiling, it will generate the following JavaScript code.

    const num =5;// numberconst str ='5';// stringconsole.log(num);console.log(str);

    Output

    5
    5
    

    Conditional Type Constraints

    Conditional type constraints are also called type assertions or conditional type predicates. It is used to add constraints on generic types. The generic types are reusable but if you want to reuse them for particular data types like array, number, etc. then you should add constraints with the generic type.

    Syntax

    You can follow the syntax below to use the conditional type constraints.

    typeConditionalType<T extends T1 | T2>=TextendsType1? TrueType : FalseType;

    In the above syntax, we have added the constraints for the type parameter 'T'. It accepts the values of type 'T1' and 'T2'.

    Example

    In the code below, 'CondionalType' is a generic type. It takes a number or string type as a value of the type parameter 'T'. The conditional type returns the number if the type of 'T' is a number. Otherwise, it returns a string.

    Next, we reused the ConditionalType type by passing the number and string as a type parameter.

    You can notice that when we try to use the 'boolean' as a type parameter, it throws an error. So, it allows to reuse of this generic conditional type with limited types.

    // Defining the conditional type with constraintstypeConditionalType<T extends number | string>=Textendsnumber?number:string;let x: ConditionalType<number>=10;let y: ConditionalType<string>='Hello';// let z: ConditionalType<boolean> = true; // Error: Type 'boolean' does not satisfy the constraint 'number | string'.console.log("The value of x is: ", x);console.log("The value of y is: ", y);

    On compiling, it will generate the following JavaScript code.

    let x =10;let y ='Hello';// let z: ConditionalType<boolean> = true; // Error: Type 'boolean' does not satisfy the constraint 'number | string'.console.log("The value of x is: ", x);console.log("The value of y is: ", y);

    Output

    The value of x is:  10
    The value of y is:  Hello
    

    Inferring Within Conditional Types

    In TypeScript, inferring within the conditional types means to infer the types with the conditional type definition. It allows you to create a more flexible type of transformation.

    For example, if you have an array of elements and match it with the type 'T' in the conditional types. After that, you want to return the type of the array element if the condition becomes true, in such case inferring conditional types is useful.

    Syntax

    You can use the 'infer' keyword to infer within the conditional types.

    typeFlatten<Type>= Type extendsArray<infer Item>? Item : Type;

    In the above syntax, the 'infer' keyword is used to extract the type of the array element.

    Example

    In the code below, we have created the 'Flatten' conditional type. We used the infer keyword with the type 'U'. It extracts the type of 'U' from the array of elements having type 'U'.

    For 'Flatten<boolean>', it will return a boolean. So, if we try to assign a string value to the 'bool1' variable, it throws an error.

    // Inferring within the conditional typestypeFlatten<T>=Textends(inferU)[]?U:T;let bool: Flatten<boolean[]>=true;// let bool1: Flatten<boolean> = "s"; // Error as string can't be assigned to booleanconsole.log(bool);

    On compiling, it will generate the following JavaScript code.

    let bool =true;// let bool1: Flatten<boolean> = "s"; // Error as string can't be assigned to booleanconsole.log(bool);

    Output

    true
    

    This way, you can use the conditional types in TypeScript. You can use the generic types, and infer them to make type transformation more flexible.

  • Indexed Access Types

    In TypeScript, Indexed access types are also known as lookup types. It is used to access the type of property of another type. It is a powerful way and helpful to access the type properties of the complex type structure. This feature allows you to use the type system more effectively, ensuring type safety in scenarios involving property access and manipulation.

    Syntax

    You can follow the syntax below to use the index access types in TypeScript.

    typenew_type= existing_type[key];
    • In the above syntax, ‘type’ is a keyword to define a new type.
    • ‘existing_type’ is an existing type.
    • The ‘key’ is a property of the existing_type whose type we need to get.

    Examples

    Let’s understand the indexed access types with the help of some examples in TypeScript.

    Example: Accessing Object Property Types

    Indexed access types are very useful when you need to get the type of the specific property of the existing type.

    In the code below, we have defined the User type which contains the id, and age properties of the number type and name property of the string type.

    The User[‘name’] is used to access the type of the ‘name’ property of the User type, which is a string.

    Next, we have defined the userName variable whose type is ‘UserNameType’ (string). If you try to assign a number to the userName variable, it will throw an error.

    typeUser={
    
    id:number;
    name:string;
    age:number;};// Access the type of 'name' property from UsertypeUserNameType= User&#91;'name'];// type is stringlet userName: UserNameType;
    userName ="Alice";// Okconsole.log(userName);// Alice// userName = 123; // Error: Type 'number' is not assignable to type 'string'.

    On compiling, it will generate the following JavaScript code.

    let userName;
    userName ="Alice";// Okconsole.log(userName);// Alice// userName = 123;   // Error: Type 'number' is not assignable to type 'string'.

    Output

    Alice
    

    Example: Accessing Nested Object Property Types

    Indexed access types are more useful when you need to get the type of any property of the complex type structure.

    In the code below, the ‘Product’ type has id and details properties. The details property is a nested type containing the name and price properties.

    We used the Product[‘details’][‘price’] to access the type of the ‘price’ property of the ‘details’ property, which is a number.

    typeProduct={
    
    id:number;
    details:{
        name:string;
        price:number;};};// Access the type of the nested 'price' propertytypeProductPriceType= Product&#91;'details']&#91;'price'];// type is numberlet price: ProductPriceType;
    price =19.99;// Correct usageconsole.log(price);// Output: 19.99// price = "20.00"; // Error: Type 'string' is not assignable to type 'number'.

    On compiling, it will generate the following JavaScript code.

    let price;
    price =19.99;// Correct usageconsole.log(price);// Output: 19.99// price = "20.00"; // Error: Type 'string' is not assignable to type 'number'.

    Output

    19.99
    

    Example: Using the keyof operator with indexed access types

    This example is similar to the first example. Here, we have used the ‘keyof’ operator to access all keys of the User type.

    When you use the Keyof operator with the type and use it as an index value, it gets the union of type of all properties of the object. Here, ‘keyof User’ gives us the number | string, which is a type of all properties of the User type.

    typeUser={
    
    id:number;
    name:string;
    age:number;};// Access the type of 'name' property from UsertypeUserNameType= User&#91;keyof User];// type is number | stringlet userName: UserNameType;
    userName ="Hello";// Correct usageconsole.log(userName);// Output: Hello userName =123;// Correct usageconsole.log(userName);// Output: 123

    On compiling, it will generate the following JavaScript code.

    let userName;
    userName ="Hello";// Correct usageconsole.log(userName);// Output: Hello
    userName =123;// Correct usageconsole.log(userName);// Output: 123

    Output

    Hello
    123
    

    Example: Indexed Access with Arrays

    When you want to access the type of the array elements, you can use the ‘number’ as an index.

    In the code below, we have defined the StringArray type, which contains the array of the string.

    The ‘StringArray[number]’ gets the type of the array element, which is a string.

    typeStringArray=string[];// Access the type of an array elementtypeElementType= StringArray[number];// type is stringlet element: ElementType;
    element ="Hello";// Correct usageconsole.log(element);// element = 123;    // Error: Type 'number' is not assignable to type 'string'.

    On compiling, it will generate the following JavaScript code.

    let element;
    element ="Hello";// Correct usageconsole.log(element);// element = 123;    // Error: Type 'number' is not assignable to type 'string'.

    Output

    Hello
    

    This way you can access the type of the properties of the existing types using the indexed access type. You can also use the ‘number’ property to access the type of the array elements.

  • Typeof Type Operator

    In TypeScript, the typeof is one of the most helpful operators and keywords to check the types of variables or identifiers. However, TypeScript is a type-strict language. So, we need to define the type of the variable or object while defining the identifier itself. Still, sometimes we need to check the type of the variable.

    For example, we are taking the data from the user via forms and handling it via TypeScript. Then we have to validate the data and perform some database operations. Also, the identifier can have multiple types in TypeScript. So, using the typeof operator, we can check the identifier type after initializing it.

    Syntax

    The below syntax demonstrates the use of typeof operator with the identifiers.

    let type = typeof <variable>
    

    Where,

    • variable is an identifier, for which we need to check the type.

    Return Value

    It returns a string according to the data type of the operand of the typeof operator.

    Examples

    Let’s understand the typeof operator with the help of examples in TypeScript −

    Example 1

    In this example, we have used the typeof operator with the string, number, boolean, and undefined variables. Also, we used the typeof operator with the variable containing the null as a value.

    In the output, we can see that It returns the string representing the type of the identifier. For the null_value1 identifier, the typeof operator returns the object.

    // Defining the variables of particular type and checking its type.let str1: string ="TutorialsPoint";
    console.log(typeof str1);let num1: number =32;
    console.log(typeof num1);let bool1: boolean =true;
    console.log(typeof bool1);let un_defined;
    console.log(typeof un_defined);let null_value1 =null;
    console.log(typeof null_value1);

    On compiling, it will generate the following JavaScript code

    // Defining the variables of particular type and checking its type.var str1 ="TutorialsPoint";
    console.log(typeof str1);var num1 =32;
    console.log(typeof num1);var bool1 =true;
    console.log(typeof bool1);var un_defined;
    console.log(typeof un_defined);var null_value1 =null;
    console.log(typeof null_value1);

    Output

    The above code will produce the following output

    string
    number
    boolean
    undefined
    object
    

    Example 2

    In the example below, we have created the objection containing some key-value pairs, a function returning the number value, and an array of numbers that contains different values.

    Also, we used the typeof operator to check the type of all. The typeof operator returns the “object” string for the object, the “function” string for the Function, and the “object” string for the array, as the array is the instance of the object.

    // using the typeof operator with object, function, and array.let demo_obj ={
       prop:"value",};
    console.log(typeof demo_obj);functionfunc(){let a =10+20;return a;}
    console.log(typeof func);let array: Array<number>=[4,23,212,2123,323,3];
    console.log(typeof array);

    On compiling, it will generate the following JavaScript code

    // using the typeof operator with object, function, and array.var demo_obj ={
       prop:"value"};
    console.log(typeof demo_obj);functionfunc(){var a =10+20;return a;}
    console.log(typeof func);var array =[4,23,212,2123,323,3];
    console.log(typeof array);

    Output

    The above code will produce the following output

    object
    function
    object
    

    Example 3

    In this example, we checked the type of the NaN keyword using the typeof operator, which returns the “number” string. The type of negative Infinity is also a number. The PI is the property of the Math class, which contains the number value. Thats why the typeof operator returns “number” for the Math.PI property.

    The pow() is the Math library function to get the power of any numbers. So, the typeof operator returns the “function” for the Math.pow() method.

    // using the typeof operator with NaN, infinity, library methods, and members.let NaN_type =NaN;
    console.log(typeof NaN_type);let infi =-Infinity;
    console.log(typeof infi);let pI = Math.PI;
    console.log(typeof pI);let pow = Math.pow;
    console.log(typeof pow);

    On compiling, it will generate the following JavaScript code

    // using the typeof operator with NaN, infinity, library methods, and members.var NaN_type =NaN;
    console.log(typeof NaN_type);var infi =-Infinity;
    console.log(typeof infi);var pI = Math.PI;
    console.log(typeof pI);var pow = Math.pow;
    console.log(typeof pow);

    Output

    The above code will produce the following output

    number
    number
    number
    function
    

    Example 4

    In this example, we used the typeof operator to get the type of variable1 and defined variable2 of the same type as variable1. Also, we used the type alias to store the type of variable3. The type named type1 contains the type of variable3, which is a string.

    After that, we used type1 to define the type of variable4. In the output, users can observe that the type of variable4 is the same as variable3.

    // defining the variable1 of type numbervar variable1: number =10;// To define the variable2 as of same type variable1let variable2:typeof variable1 =20;
    console.log(typeof variable2);// variable3 of string typevar variable3: string ="Hello!";// using the type alias to define the type of same type as variable3
    type type1 =typeof variable3;// defining the variable4 of type named type1let variable4: type1 ="Hi";
    console.log(typeof variable4);

    On compiling, it will generate the following JavaScript code

    // defining the variable1 of type numbervar variable1 =10;// To define the variable2 as of same type variable1var variable2 =20;
    console.log(typeof variable2);// variable3 of string typevar variable3 ="Hello!";// defining the variable4 of type named type1var variable4 ="Hi";
    console.log(typeof variable4);

    Output

    The above code will produce the following output

    number
    string
    
  • Keyof Type Operator

    In TypeScript, the keyof type operator allows you to obtain the keys of an object and use them to perform various operations. It plays a significant role when working with objects and their properties.

    Syntax

    The syntax of the keyof type operator in TypeScript is as follows

    keyof Type
    

    The keyof keyword is followed by the name of a type, referred to as “Type.” It returns a union type consisting of all the keys (property names) of the specified type. This allows you to access and manipulate an object’s keys dynamically.

    Examples

    Let’s understand the keyof operator with the help of some examples in TypeScript.

    Example 1: Accessing Object Property Names

    The keyof keyword helps us retrieve the names of properties defined within an object type.

    In this example, we define an interface called “Person” with two properties: “name” of type string and “age” of type number.

    Then we create a type alias “PersonKeys” using keyof to extract the keys from the Person interface.

    Finally, we assign the value ‘name’ to a constant named “keys” of type PersonKeys and log its value.

    interfacePerson{
       name: string;
       age: number;}
    
    type PersonKeys = keyof Person;const keys: PersonKeys ='name';
    console.log(keys);

    On compiling, it will generate the following JavaScript code

    var keys ='name';
    console.log(keys);

    Output

    The above code will produce the following output

    name
    

    Example: Type-Safe Property Access

    The keyof keyword enables us to access object properties in a type-safe manner.

    In this example, we define an interface “Car” with properties representing a car’s brand, year, and price. We declare a generic function called “getProperty” that takes two arguments: “obj” of type T and “key” of type K, where K extends keyof T. The function returns the value corresponding to the given key in the object. We then create a car object and use the “getProperty” function to retrieve the car’s brand property and assign it to the “carBrand” variable. Finally, we log the value of “carBrand.

    interfaceCar{
       brand: string;
       year: number;
       price: number;}function getProperty<T,KextendskeyofT>(obj:T, key:K):T[K]{return obj[key];}const car: Car ={
       brand:'Toyota',
       year:2022,
       price:25000,};const carBrand: string =getProperty(car,'brand');
    console.log(carBrand);

    On compiling, it will generate the following JavaScript code

    functiongetProperty(obj, key){return obj[key];}var car ={
       brand:'Toyota',
       year:2022,
       price:25000};var carBrand =getProperty(car,'brand');
    console.log(carBrand);

    Output

    The above code will produce the following output

    Toyota
    

    Example 3: Mapping Object Properties

    The keyof keyword is also useful for mapping properties from one object to another. In this example, we define an interface “Fruit” with properties representing the name and color of the fruit. We then create a new type, “FruitMapped” using the keyof within a mapped type. It maps each key in keyof Fruit to the type “string”. Finally, we create a “fruit” object using the FruitMapped type and log its value.

    interfaceFruit{
       name: string;
       color: string;}
    
    type FruitMapped ={[Kin keyof Fruit]: string;};const fruit: FruitMapped ={
       name:'Apple',
       color:'Red',};
    
    console.log(fruit);

    On compiling, it will generate the following JavaScript code

    var fruit ={
       name:'Apple',
       color:'Red'};
    console.log(fruit);

    Output

    The above code will produce the following output

    { name: 'Apple', color: 'Red' }
    

    Example 4: Conditional Type Mapping

    Another powerful application of the keyof keyword is in conditional type mapping. This allows us to selectively map properties based on certain conditions. In this example, we have an interface “Product” representing a product’s name, price, and availability. We define a type “DiscountedProducts” that utilizes conditional mapping using keyof. It checks if the property’s value extends the “number” type and maps it to the union type of the original value and “null”. For all other property types, it keeps the original value type unchanged. Finally, we create a “product” object using the DiscountedProducts type.

    interfaceProduct{
       name: string;
       price: number;
       inStock: boolean;}
    
    type DiscountedProducts<T>={[Kin keyof T]:T[K]extendsnumber?T[K]|null:T[K];};const product: DiscountedProducts<Product>={
       name:'Widget',
       price:10,
       inStock:true,};
    
    console.log(product);

    On compiling, it will generate the following JavaScript code

    var product ={
       name:'Widget',
       price:10,
       inStock:true};
    console.log(product);

    Output

    The above code will produce the following output

    { name: 'Widget', price: 10, inStock: true }
    
  • Creating Types from Types

    TypeScript allows you to create custom types from the existing or built-in types. Creating types from existing types helps you to make code more maintainable and error-resistant.

    TypeScript is a statically typed language. This means that ideally, each variable, function parameter, and object property should be explicitly assigned a type.

    Here we will discuss different ways to create custom types from the existing types.

    Union Types

    If you want to define a single variable that can store the value of multiple data types, you can use the union operator to combine multiple operators. The union operator is ‘|’, and using that you can combine multiple types.

    Syntax

    You can follow the syntax below to create custom types by using the union operator.

    typeunionTypes= type1 | type2 | type3 |...;

    In the above syntax, type1, type2, type3, etc. are data types.

    Example

    In the code below, we have defined the ‘StringOrNumber’ custom type which is a union of the string and number data type.

    The processValue() function takes the single parameter of the type ‘StringOrNumber’. In the function body, we check the type of the parameter using the ‘typeof’ operator, and based on the string or numeric type, we print the value in the output console.

    // Defining a union typetypeStringOrNumber=string|number;// Function that accepts a union type as an argumentfunctionprocessValue(value: StringOrNumber){// Check if the value is a string or number typeif(typeof value ==="string"){console.log(String: ${value});}else{console.log(Number: ${value});}}processValue("hello");// Output: String: helloprocessValue(123);// Output: Number: 123

    On compiling, it will generate the following JavaScript code.

    // Function that accepts a union type as an argumentfunctionprocessValue(value){// Check if the value is a string or number typeif(typeof value ==="string"){console.log(String: ${value});}else{console.log(Number: ${value});}}processValue("hello");// Output: String: helloprocessValue(123);// Output: Number: 123

    The output of the above example code is as follows

    String: hello
    Number: 123
    

    Intersection Types

    The intersection operator (&) allows you to combine the multiple types in the single types. For example, if you have two interfaces or types for Business and contactDetails, and want to combine both types, you can use the intersection operator.

    Syntax

    You can follow the syntax below to create custom types by using the intersection operator.

    typeintersectionTypes= type1 & type2 & type3 &...;

    In the above syntax, we have combined multiple types using the intersection operator.

    Example

    In the code below, we have defined the ‘Business’ interface containing the name and turnover properties. We have also defined the contactDetails interface containing the email and phone properties.

    After that, we combined both types using the intersection operator and stored them in the ‘BusinessContact’ type. Next, we have defined the object of the type ‘BusinessContact’, and logged it in the console.

    // Defining a business typeinterfaceBusiness{
    
    name:string;
    turnover:number;}// Defining a contact details typeinterface ContactDetails {
    email:string;
    phone:string;}// Intersection of two typestypeBusinessContact= Business &amp; ContactDetails;// Creating an object of the type BusinessContactlet contact: BusinessContact ={
    name:"EnviroFront",
    turnover:5000000,
    email:"[email protected]",
    phone:"1234567890"};console.log(contact);</code></pre>

    On compiling, it will generate the following JavaScript code.

    // Creating an object of the type BusinessContactlet contact ={
    
    name:"EnviroFront",
    turnover:5000000,
    email:"[email protected]",
    phone:"1234567890"};console.log(contact);</code></pre>

    The output of the above example code is as follows

    {
      name: 'EnviroFront',
      turnover: 5000000,
      email: '[email protected]',
      phone: '1234567890'
    }
    

    Utility types

    TypeScript contains multiple utility types, which makes it easy to transform the particular types and create new types. Let's look at some of the utility types with examples.

    Syntax

    You can follow the syntax below to use any utility type.

    Utility_type_name<typeorinterface>

    In the above syntax, 'utility_type_name' can be replaced with 'Partial', 'Pick', 'Record', etc. based on which utility type you are using. Here, 'type' or 'interface' is the name of the interface from which you want to create a new custom type.

    Partial Utility Type

    The partial utility type makes all properties of the interface or type option. So, you can create a new type having all properties optional from the existing type.

    Example

    In the code below, we have defined the 'Todo' interface. After that, we used the Partial utility type to create a new type using the Todo interface by making all properties of it optional.

    Next, we have defined the object of the 'OptionalTodo' type, which contains only the 'title' property, as the 'description' property is optional.

    // Defining an interface for Todo list interfaceTodo{
    
    title:string;
    description:string;}// Defining a custom type using the Partial utility typetypeOptionalTodo= Partial&lt;Todo&gt;;// Creating an object of type OptionalTodolet todo: OptionalTodo ={ title:"Buy milk"};// 'description' is optionalconsole.log(todo);</code></pre>

    On compiling, it will generate the following JavaScript code.

    // Creating an object of type OptionalTodolet todo ={ title:"Buy milk"};// 'description' is optionalconsole.log(todo);

    The output of the above example code is as follows

    { title: 'Buy milk' }
    

    Pick Utility Type

    The Pick utility type allows one to pick a subset of properties from the existing types. Let's understand it via the example below.

    Example

    In the code below, we have defined the 'ToDo' interface and used the Pick utility type to create a new type with only the 'title' property from the 'ToDo' interface. Here, the 'ToDoPick' type contains only the 'title' property.

    // Defining an interface for Todo list interfaceTodo{
    
    title:string;
    description:string;}// Defining a custom type using the Pick utility typetypeTodoPick= Pick&lt;Todo,"title"&gt;;let myTodo: TodoPick ={ title:"Write a code"};console.log(myTodo.title);</code></pre>

    On compiling, it will generate the following JavaScript code.

    let myTodo ={ title:"Write a code"};console.log(myTodo.title);

    The output of the above example code is as follows

    Write a code
    

    Typeof Type Operator

    The typeof operator is used to get the type of the particular variable. You can use the typeOf variable to extract the data type of the particular variable, object, function, etc., and use the data type for other variables.

    Syntax

    You can follow the syntax below to create custom types by using the typeof type operator.

    Typeof variable;

    In the above syntax, 'variable' can be an object, function, etc.

    Example

    In the code below, we have defined the person object containing the name and gender properties. After that, we used the 'typeof' operator to get the type of the person object and stored it in the 'employee' type variable.

    After that, we have defined the 'employee1' object of the 'employee' type, which contains the name and gender properties.

    // Defining a person objectconst person ={
    
    name:"John",
    gender:"male"};// Defining a type employeetypeemployee=typeof person;// Type is { url: string; timeout: number; }// Defining an employee objectlet employee1: employee ={
    name:"Sbv",
    gender:"male"};console.log(employee1);</code></pre>

    On compiling, it will generate the following JavaScript code.

    // Defining a person objectconst person ={
    
    name:"John",
    gender:"male"};// Defining an employee objectlet employee1 ={
    name:"Sak",
    gender:"male"};console.log(employee1);</code></pre>

    The output is as follows

    { name: 'Sak', gender: 'male' }
    

    Creating a custom type allows you to reuse the existing types and write maintainable code. You may use the union or intersection operator if you want to provide an optional type or combine multiple types. Furthermore, you can use the utility types and typeof operator to create custom types from the existing types.