TypeScript Notes
( TypeScript Roadmap : Click )
TypeScript is a strongly typed, object oriented, compiled language. It was designed by Anders Hejlsberg (designer of C#) at Microsoft. TypeScript is both a language and a set of tools. TypeScript is a typed superset of JavaScript compiled to JavaScript. In other words, TypeScript is JavaScript plus some additional features like Types and Object-Oriented Programming. TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. It offers classes, modules, and interfaces to help you build robust components. TypeScript uses compile-time type checking. Which means it checks if the specified types match before running the code, not while running the code.
NOTE : In regular Javascript since we dont have types we could acess any property or function of objects with the dot operator (".") even if that property or function is not available on that variable. This way alot of errors would bypass the compilation process and only to be caught at runtime. With Typescript , since we add types to each variables, such runtime errors are avoided as we can only acess properties or functions available to a specific data type.
Some of the benefits of using TypeScript are as followed :
- Makes code more readable to developers : TypeScript's type system helps to document the code and make it easier to understand, especially for developers who are new to the project.
- Adds a type system to JavaScript : TypeScript extends JavaScript by adding a type system, which helps to catch type-related errors at compile time instead of at runtime. This helps to prevent unexpected bugs and makes the code more robust.
- Catches errors early at compile time rather than at runtime : By catching type-related errors early in the development process, TypeScript helps to save time and effort that would otherwise be spent debugging and fixing errors at runtime.
TypeScript code cannot be executed on any browser directly. The program written in TypeScript always ends with JavaScript. Hence, we only need to know JavaScript to use it in TypeScript. Transpilers or source-to-source compilers, are tools that read the sourcecode written in one programming language and produce the equivalent code in another programming language.
Typescript also has a Transpiler of its own to convert Typescript to Javascript.The code written in TypeScript is compiled and converted into its JavaScript equivalent for the execution. This process is known as Transpilation.
NOTE : The file extension for typescript files is ".ts" extension, which after transpilation generates the regular ".js" file.
---------------------------------------------------------------------------------------------------------------
(Useful: 1] Click)
Typescript Compiler (TSC)
Tsc stands for `TypeScript compiler` and is a simple CLI tool included in Typescript itself, allowing you to compile ts files into js. Below are some common tsc commands.
tsconfig.json
The tsconfig.json file corresponds to the configuration of the TypeScript compiler (tsc). It is a configuration file in TypeScript that specifies the compiler options for building your project. It helps the TypeScript compiler understand the structure of your project and how it should be compiled to JavaScript.
It also signifies that the directory in which it is kept is the root of TypeScript project. The tsconfig.json file specifies the root files and the compiler options required to compile the project. The TSC is expected to be executed based on the options mentioned inside the config file.
Below are some important options to be mentioned inside the tsconfig.json file :
- target - The javascript version to which .ts files are transpiled, default is ES6.
- rootDir - Path to the folder where .ts files are stored.
- outDir - Path to the folder where JS files should be stored after transpilation.
- allowJS - Tells the TypeScript compiler to process JavaScript files as well as TypeScript files, allowing us to use regular Javascript with Typescript.
- module - Sets which module system to use (CommonJs, UMd, AMF, Ecmascript). Use commonJs for NodeJs projects.
- moduleResolution - Sets the module resolution strategy to use during transpilation.
- removeComments - Strips all comments from TypeScript files when converting into JavaScript.
TS-Node
Traditionally, TypeScript code needs to be compiled to JavaScript before it can be executed in a Node.js environment. However, with "ts-node", TypeScript code can be executed directly in a Node.js environment, as "ts-node" compiles TypeScript on-the-fly, meaning that it dynamically converts TypeScript code to JavaScript at runtime, without the need for a separate compilation step.
Another added bonus to ts-node is being able to use a TypeScript REPL (read-evaluate-print loop) similar to running node without any options. This TypeScript REPL allows you to write TypeScript in command-line and is super handy for quickly testing something out.
---------------------------------------------------------------------------------------------------------------
Type Annotation/Assertion
TypeScript is a typed language, where we can specify the type of the variables, function parameters and object properties. We can specify the type by using the ":type" after the name of the variable, parameter or property. There can be a space after the colon.
Type annotations are used to enforce type checking. It is not mandatory in TypeScript to use type annotations. However, type annotations help the compiler in checking types and helps avoid errors dealing with data types.
---------------------------------------------------------------------------------------------------------------
Primitive Data Types
Like JavaScript and any other language, TypeScript also provides basic data types to handle numbers, strings, etc. Some common data types in TypeScript are as followed :
- String - It is used to represent a sequence of characters.
- Number - It represents both Integer as well as Floating-Point numbers.
- Boolean - Represents true and false
- Any - If variable is declared with "any" data-type then any type of value can be assigned to that variable. Use it to skip type checks like regular JS.
- Unknown - Similar to "any" but alot less permissive than "any" data type.
- Never - It type represents the data type of values that never occur.
- Null & Undefined - It is used when an object does not have any value.
- Void - Generally used on function return-types if a function does not return any value.
NOTE : undefined is a variable that refers to something that doesn't exist, and the variable isn't defined to be anything. null is a variable that is defined but is missing a value.
Any & Unknown Type
The any type is essentially an escape hatch from the type system. As developers, this gives us a ton of freedom. TypeScript lets us perform any operation we want on values of type any without having to perform any kind of checking beforehand. Because of that, TypeScript considers all of the following operations on an "any" type variable to be type-correct by default. In many cases, this is too permissive. Using the any type, it's easy to write code that is type-correct, but problematic at runtime. We don't get a lot of protection from TypeScript if we're opting to use any.
The 'unknown' type is similar to the 'any' type in a way that we can assign any value to it. There are 2 major differences 'unknown' and 'any' which are as followed :
- The variable of 'unknown' type is only assignable to variables of 'any' type or 'unknown' type itself, this makes sense after all, we don't know anything about what kind of value is stored.
- We cannot perform arbitrary operations on 'unknown' type variable since none of these operations are type-correct anymore and we can’t access any properties on unknown values, nor can we call or construct them.
You can't operate directly on variables of type unknown. We have to give TypeScript information to narrow the type so that it can be used. Typescript doesn't allow you to use a variable of unknown type unless you either cast the variable to a known type or narrow its type. Type narrowing is the process of moving a less precise type to a more precise type.
In summary, a variabe of type 'unknown' is similar to any but not operable until we narrow down its data type.
Never Type (Useful : 1] Click)
The "never" type represents the data type of values that never occur. The never type is used when you are sure that something is never going to occur. The never type is a type that contains no values. Because of this, you cannot assign any value to a variable with a never type.
Typically, you use the never type to represent the return type of a function that always throws an error or a function that never returns at all and has no reachable point and there mainly 2 cases where a function has no reachable point :
- A Function only throws an Error.
- A Function is running an indefinite loop inside it.
In a function expression or arrow function with no return type annotation, if the function has no return statements, or only return statements with expressions of type never, and if the end point of the function is not reachable, the inferred return type for the function is never.
- A function that doesn't explicitly return a value implicitly returns the value
undefinedin JavaScript. Although we typically say that such a function "doesn't return anything", it returns. We usually ignore the return value in these cases. Such a function is inferred to have avoidreturn type in TypeScript. - A function that has a
neverreturn type never returns. It doesn't returnundefined, either. The function doesn't have a normal completion, which means it throws an error or never finishes running at all.
---------------------------------------------------------------------------------------------------------------
Type Inference
TypeScript is a typed language. However, it is not mandatory to specify type of every variable. TypeScript compiler can deduce the types of variables for us when there is no explicit information available in the form of type annotations, this process is called Type Inference, where we rely on Typescript to get the types right. Types are inferred by TypeScript compiler when :
- Variables are initialized
- Default values are set for parameters
- Function return types are determined
For more complex objects like Arrays, TypeScript compiler looks for the most common type to infer the type of the object. If it does not find any super type that can encompass all the types present in the array. In such cases, the compiler treats the type as a union of all types present in the array.
Contextual Typing
Typescript also has the ability to infer the type of variables based on their context or location. The best example is when we type event objects.
---------------------------------------------------------------------------------------------------------------
Functions
Typescript has a specific syntax to define functions with arguments and return types. The syntax for creating functions in TypeScript is the same, except for one major addition: You can let the compiler know what types each argument or parameter should have.
NOTE : If no return type is defined, TypeScript will attempt to infer it through the types of the variables or expressions returned.
NOTE : If no parameter type is defined, TypeScript will default to using "any"data type.
Function Overloading
Function Overloading in TypeScript allows multiple functions with the same name but with different parameters to be defined. The correct function to call is determined based on type, order & number of arguments passed to the function at runtime.
---------------------------------------------------------------------------------------------------------------
Non-Primitive Data Types
Below are some common non-primitive data types supported in Typescript :
- Array
- Tuples
- Enum
- Objects
Arrays
An array is a homogenous collection of similar type of elements which have a contiguous memory location. In Typescript we can define what type of values can be pushed inside the arrays by definig their data type.
NOTE : The readonly keyword can prevent arrays from being changed, so we'll get an error if we try to push something or change any value inside of it.
Tuples
A tuple is an array with a pre-defined length which allows us to store a collection of values of varied types which could'nt be done with arrays.
We can also explicitly define the types of each index element. If you do so you have to maintain the order of elements.
The readonly keyword can prevent tuple from being changed, so we'll get an error if we try to push something or change any value inside of it.
Enums
In TypeScript, enums, or enumerated types, are data structures of constant length that hold a set of constant values. Each of these constant values is known as a member of the enum. Enums are useful when setting properties or values that can only be a certain number of possible values. There are 3 types of enums in Typescript, which are as followed :
- Numeric Enums - These store values as numbers. By default, enums will initialize the first value to 0 and add 1 to each additional value.
- String Enums - These store string values and need to be initialized with values.
- Heterogeneous Enums - These contain both strings and numbers as values.
Example] Below the getCheese() can only accept values present inside enum.
Object Type
An object is an instance which contains set of key value pairs. The values can be scalar values or functions or even array of other objects. When adding a property to the object, you also need to define the type of each property.
We can also create "Optional Properties" by defining them inside the types with an "?" but not inside the object values, and later add them dynamicaly.
The "in" keyword
In TypeScript, the in keyword is used to check if a property exists in an object. It is often used in conjunction with the if statement as type guard.
---------------------------------------------------------------------------------------------------------------
TypeOf Operator / Type Guard
A typeOf keyword returns the type of an identifier in TypeScript. It also acts as a Type Guard narrowing the type of the variable in the scope where we use it. It is used to check the type of a variable. It returns a string representing the type of the variable.
Let's say we want to perform different actions based upon whether input is a number or a string. In this case, we will use Javascripts type guards to check if it's a string or number, as shown below.
---------------------------------------------------------------------------------------------------------------
Custom Types
In TypeScript, an interface and a type alias are 2 different ways of defining a custom type with given name. While both interfaces and type aliases can be used to define custom types, interfaces are typically used to define object shapes, while type aliases are more commonly used to define union types, complex types, and other types that don't fit the object shape pattern.
1] Type Aliases
TypeScript allows types to be defined separately from variables that use them. Type Aliases allow defining types with a custom name (an Alias) which can be also reused again. Type Aliases can be used for primitives like string or more complex types such as objects and arrays. We use the "type" keyword to create an type alias.
We can also combine multiple type aliases to create a new type alias using the "&" intersection operator as shown below.
2] Interfaces
An interface is an OOP concept and is similar to Type alias except it only applies to objects. TypeScript allows you to specifically add type to objects using an interface that can be reused by multiple objects. Interfaces in TypeScript are created by using the "interface" keyword.
We can combine multiple interfaces using "extends" keyword, as shown below :
---------------------------------------------------------------------------------------------------------------
Union Type
The Union Type allows us to use more than one data type for a variable or a function parameter. Union Types in TypeScript allow you to specify multiple possible types for a single variable or parameter. A union type is written as a vertical bar "|" separated list of types.
We can also create more flexible types with the help of Unions in Typescript, such that if we have two different types of objects that share some similar properties, we can define a union type of them so the resulting type can contain any combination of properties from either of types.
Intersection Type
An intersection type creates a new type by combining multiple existing types. The new type has all features of the existing types. To combine types, you use the "&" operator.
---------------------------------------------------------------------------------------------------------------
Explicit and Implicit Assignment
Using TypeScript can provide many benefits, such as reducing bugs or self-documenting codebase. But it can also cause dilemmas, such as whether to allow TypeScript to infer types on its own or to explicitly annotate the type, as in other programming languages.
There are 2 ways to add type annotations in Typescript as followed :
- Explicit Type Annotation - It's when we ourself provide the type using a special TypeScript syntax of ":" colon mark.
- Implicit Type Annotation -It's when TypeScript infers the type on its own without us defining any type, based on a variable's initial value and look just like regular JavaScript.
NOTE : The Implicit Type annotation may look like regular Javascript but it's Typescript as in any case the variables and objects are Type-safe.
---------------------------------------------------------------------------------------------------------------
Utility Types
In TypeScript, utility types are pre-defined type transformations that allow developers to easily manipulate and transform existing types. Some of the most commonly used utility types in TypeScript are the following :
Partial<T>: It creates a new type that has all the properties of the original typeT, but all the properties are optional.
Required<T>: It creates a new type that has all the properties of the original typeT, but all the properties are required.
Readonly<T>: It creates a new type that has all the properties of the original typeT, but all the properties are read-only.
Pick<T,K>: It creates a new type that has only the specified propertiesKof the original typeT.
Omit<T,K>: It creates a new type that has all the properties of the original typeT, except for the specified propertiesK.
1] Partial<T>
By default win Typescript, an object must specify values for all the properties mentioned inside its Type. The Partial type in TypeScript allows you to make all properties of a type optional. This is useful when you need to create an object with only a subset of the properties of an existing type.
2] Required<T>
The Required<T> is a utility type in TypeScript that makes all properties of a given type T required. It's useful when you have an object or a function that requires all properties to be present, and you want to ensure that all required properties are provided.
3] ReadOnly<T>
Readonly constructs a type with all properties of Type set to readonly, meaning the properties of the constructed type cannot be reassigned.
4] Pick<T,K>
The Pick<T,K> is a utility type in TypeScript that creates a new type by picking a subset of properties from an existing type T, based on a set of property names K. It is useful when we want to accept an object with only some of its properties.
5] Omit<T,K>
The Omit<T, K> is a utility type in TypeScript that creates a new type by omitting a subset of properties from an existing type T, based on a set of property names K. It is useful when we want to accept an object but exclude some of its properties.
---------------------------------------------------------------------------------------------------------------
Classes
TypeScript is object oriented JavaScript. TypeScript supports object-oriented programming features like classes, interfaces, etc. A class in terms of OOP is a blueprint for creating objects. A class encapsulates data for the object. Typescript gives built in support for this concept called class. JavaScript ES5 or earlier didn’t support classes. Typescript gets this feature from ES6.
In Typescript you use the "class" keyword to define a class, and the "new" keyword to create instances of that class. You can use the "constructor" keyword to define constructor function for the class which is executed everytime a new instance is created.
NOTE : In TypeScript, when defining attributes or methods inside a class, you don't need to explicitly use the var, let, or const keywords to declare attributes, nor do you need to use the function keyword to define methods.
Override Methods
The derived class can override the methods of parent class using the "override" keyword.
Inheritance
Just like in other programming languages, Typescript also provides us a way to inherit from other classes and Interfaces. We use the keyword "Implement" to inherit from Interfaces and "extends" to inherit from a class.
1] Implementing an Interface
If you implement an Interface then the implementing class should strictly define all the properties and the functions of the Interface with the same name and data type inside its own class. If the implementing class does not follow the structure, then the compiler will show an error.
NOTE : A class can inherit multiple interfaces but it can only inherit from one class.
2] Inheriting an class
We use the "extends" keyword to inherit from some other predefined class.
NOTE : The class whose members are inherited is called the "base class", and the class that inherits those members is called the "derived class".
If the base class consist a constructor function then the derived class must call the "super()" method inside it's constructor before using "this" keyword., it'll execute the constructor of parent class and initializes it's data memebers.
Access/Visibility Modifiers
A class can control the visibility and access of its data members, this is done using access modifiers. There are 3 types of access modifiers in TypeScript :
1] Public (default) - By default, all members of a class in TypeScript are public. All the public members can be accessed anywhere without any restrictions.
2] Private - The private access modifier ensures that class members are visible only to that class and are not accessible outside the containing class.
3] Protected - A protected class member can be only be accessed from its containing class and inside the class that inherit it.
NOTE : If we add access modifiers to constructor arguments, they become class properties without us initializing them.
Static Members
When we use the "static" keyword on properties we define on a class, they belong to the class itself. That means that we cannot access those properties from an instance of the class. We can only access the properties directly by referencing the class itself.
Abstract Class
An Abstract class is a class which cannot be initiated i.e we cannot create its instances but it can be inherited by other classes. It contain abstract and non-abstract methods in it, and the derived class must implement all abstract methods. An Interface is also an example of abstract class but with only abstract methods. To declare an abstract class, you use theabstractkeyword.
NOTE : Abstract methods can only occur in an abstract class, that's why we also mark the class with "abstract" class.
InstanceOf Operator
The instanceOf operator is a way to narrow down the type of a variable. It is used to check if an object is an instance of a class.
---------------------------------------------------------------------------------------------------------------
Type Assertions & Type Casting
The term "type assertion" refers to the process of telling the TypeScript compiler to treat a value as if it has a specific type, even if the compiler cannot verify that the value has that type at compile-time. Type assertion is only relevant at compile-time, and does not affect the actual value or behavior of an object at runtime.
In a sense, you can think of type assertion as "fooling" the TypeScript compiler into treating a value as if it were of a specific type during compilation, even if TypeScript's type inference algorithm cannot determine the exact type of the value.
There are 2 ways to apply type assertions in TypeScript as follows :
- The “angle-bracket” syntax :
<T>value - The “as” syntax : value as
T
NOTE : Type assertion is different than Type Casting or Conversion. Assertion only tells the compiler that an object is of specific type but does'nt actually convert it.
When you use type assertion to tell TypeScript the type of a value or expression, you are essentially overriding TypeScript's type inference algorithm for that specific value or expression. TypeScript will use the asserted type for type checking and type compatibility checks at compile-time, but the actual value of the object or value will not be modified or affected in any way.
NOTE : If you really want to perform type conversion and not just asert types, you can use built-in functions like the ".toString()", "Number()", "Boolean()" etc
A common use case for type assertion is when working with the DOM in TypeScript. The DOM API often returns elements or attributes as Element or HTMLElement, which are too general types in TypeScript. In such cases we can use type assertion to tell TypeScript the specific type of the element or attribute.
NOTE : In TypeScript, type assertions can only be used with any or unknown types, as they are the only types that are flexible enough to allow any value to be assigned to them. If you want to assert a variable to a specific type, but the variable already has a specific type (or its type is inferred by TypeScript), you need to first convert it to any or unknown type before you can assert it again to your specific type.
By using unknown or any as an intermediary step, you can help ensure that the final type assertion is more accurate and less likely to result in runtime errors or unexpected behavior.
NOTE : The <> type of casting will not work with TSX, such as when working on React files as JSX components have similar syntax.
Non-Null Assertion Operator (!)
The non-null assertion operator (!) is a type assertion in TypeScript that allows you to tell the compiler that a value will never be null or undefined. In some cases, TypeScript may infer a type as potentially nullable at compile-time, even when you know that it won't be null or undefined.
By using the non-null assertion operator, you can tell TypeScript to treat the value as non-null, and the compiler will allow you to access its properties or call its methods without checking for null or undefined.
It's worth noting that the non-null assertion operator should only be used when you are absolutely certain that the value is non-null or non-undefined. If the value is actually null or undefined at runtime, using the non-null assertion operator can lead to runtime errors or unexpected behavior.
Optional Chaining Operator
Optional chaining is a feature in JavaScript that allows you to safely access nested properties of an object without worrying about whether any of those properties are null or undefined. It uses the question mark (?.) operator to check if a property exists before attempting to access it's nested properties.
Without optional chaining, if we try to access a nested property of an object that is 'null' or 'undefined', we would get a runtime 'TypeError' and the program would stop executing. This can be especially problematic when dealing with deeply nested objects or objects that may have optional properties. Without optional chaining, we would need to manually use a lot of "if" statements to check for the existence of nested properties in an object.
By using optional chaining, we can safely access nested properties without worrying about whether they are 'null' or 'undefined'. If a property does not exist, the expression simply returns undefined and the program continues executing.
NOTE : If a property or method accessed using optional chaining is null or undefined, the expression will short circuit and return undefined without evaluating the rest of the expression.
Nullish Coalescing Operator
The Nullish Coalescing operator (??) is a new operator introduced in ECMAScript 2020, used to provide a fallback value for variables that may be null or undefined. If the value is either 'null' or 'undefined', the operator returns the fallback value. If the value is not 'null' or 'undefined' the operator returns the actual value.
NOTE : The Nullish Coalescing operator ?? is particularly useful when working with data fetched from an API because the API may not always return the data we expect. In cases where the data is missing or nullish, we can use the Nullish Coalescing operator to provide a default value.
---------------------------------------------------------------------------------------------------------------
Generics
Generics in OOP allow us to define a specification of a class or function that can work with multiple data type. When we design a generic, the data types of the function parameters or class isn't known - not until it is called or instantiated. The generics allow us to write code which is general and can be used for any data type, preventing us from rewriting the same code for multiple data types.
Generics allow you to write functions, classes, and interfaces that take one or more type parameters, which act as placeholders for the actual data types that will be used when the function, class, or interface is used.
Example] Below we write a generic function which can work with string and number type parameters at the same time.
The generic type parameter is specified in angle brackets (<>) after the name of the class or function. A generic class can have generic fields (member variables) or methods.
Example] Below we write a generic object which can work with strings, numbers and array type properties at the same time.
NOTE : In generics, type narrowing is not compulsory but Typescript avoids us from directly performing arbitrary operations on inputs of 'any' data type and enforces us to narrow down the types through Type guards and conditionals.
Example] We can also create complex interfaces with generics as shown below.
---------------------------------------------------------------------------------------------------------------
Modules in Typescript
A module is a way to create a group of related variables, functions, classes, and interfaces, etc, which can be imported into other modules or Typescript files. In TypeScript, files containing a top-level export or import are considered modules. A module can be created using the keyword export and a module can be used in another module using the keyword import.
NOTE : In TypeScript, files containing a top-level export or import are considered modules, other files are considered regular scripts.
Example] Below we export functions and other objects in a module and import them into another file to be reused again.
NOTE : A TypeScript module can contain both type declarations and code, which means that you can define types, interfaces, and other declarations inside a module, and also include executable code that uses those declarations. When you import a module that contains both declarations and executable code, the executable code will be executed every time you import the module.
Example] Below when we execute the main.js file, the executable code inside the utils.js file also gets executed first since we imported from it.
NOTE : The "module" option in the tsconfig.json file allows you to control what module system the generated JavaScript code uses. There are several module systems that you can choose from such as CommonJS, AMD, UMD, ES6 modules, and SystemJS. By default, TypeScript uses the CommonJS module system for generating code when targeting Node.js, and ES6 modules when targeting modern web browsers.
---------------------------------------------------------------------------------------------------------------
Type Definition / Declaration Files
In TypeScript, type declaration files (".d.ts" files) are files that contain & export type information for libraries or code that don't have built-in TypeScript support. They provide a way to describe the shape of JavaScript code, including its functions, classes, objects, and other entities.
A Type declaration file in TypeScript contains only type definitions and no actual implementation. This means that it defines the structure and types of the code, but not the actual logic of the code. These type declarations provide information about the types of parameters, return types, and other properties of the code.
For example, let's say you have a JavaScript library that provides a function called doSomething(). In order to use this function in TypeScript, you would need to create a type declaration file that describes the function's parameters and return type.
Below are some rules to follow when creating type definitions in TypeScript :
- Type declaration files must have the "
.d.ts" file extension to indicate that they contain only type declarations and no executable code. It is short for "declaration file typescript" and indicates that the file is a TypeScript declaration file.
- If the definition file is for a module, the filename should be the same as the module name with a
.d.tsextension. For example, if you have a module named "my-module", the definition file should be named "my-module.d.ts". This way, you don't need to import types from the definition file, as TypeScript will automatically understand to use the type definitions from the declaration file.
- Always export the type definitions with the "
export" keyword to make them available for use outside of the module where they are defined. If you don't need to use a type or interface outside of the module where it is defined, you can omit theexportkeyword and still use the type or interface within the same module.
- If TypeScript is unable to find your type definitions, it will try to infer the types on its own and most likely set the
anytype for the imported JavaScript code. To avoid this, make sure your type definitions are correctly exported and available in the scope where they are needed.
Example] Below we define declaration file for JS module to use it inside TS..
TypeScript comes with number of built-in type declaration files for popular libraries and APIs such as the DOM, Node.js, and many others. These type declaration files are included with the TypeScript compiler and are automatically used to provide type-checking when you write TypeScript code that interacts with these libraries or APIs.
For third-party NPM packages, most come with their own type declaration files. If a package includes its own type declaration file, it will typically be located at path "node_modules/<package>/index.d.ts" at the root of the package. If the package does not include a type declaration file, you may need to create one yourself, or use a community-contributed type declaration file.
The "declare" keyword
The "declare" is an optional keyword often used inside type definition files. In TypeScript, the declare keyword is used to declare the type and shape of variables, functions, classes, and interfaces that are defined in other parts of your code, or in third-party libraries and APIs.
When you use the declare keyword, you're telling TypeScript that the value or entity you're declaring exists, but you're not providing a concrete implementation for it. This is because the implementation may be located in a separate module or library, or it may be dynamically generated at runtime.
Example] Below we define declaration file for JS module to use it inside TS.
DefinitelyTyped
Most of the Javascript libraries today support Typescript or atleast come with their own declaration files which we can use to add in our project. Some packages don't support Typescript or don't come with a type definition file, in such cases we can either write our own type definition file or find one and include it in our project.
To help TypeScript developers use such packages, "Definitely Typed" is a project that provides a central repository of TypeScript definition files for commonly used npm packages, it provides a searchable directory of available type declaration files that you can use in your TypeScript projects.
NOTE : For the most part, type declaration packages should always have the same name as the package name on npm, but prefixed with @types/, but if you need, you can use the npm package search to find the package for your favorite library.
NOTE : Many of these popular packages, such as React, Redux, and Moment.js, already include their own type definitions, which means you can use them without the need to install any additional @types packages. However, installing @types packages can still be helpful in cases where the package does not provide its own type definitions, or if you need to use a version of the package that doesn't have type definitions included.
---------------------------------------------------------------------------------------------------------------
Typescript/JS Interoperability
TypeScript and JavaScript have full interoperability, meaning you can use TypeScript code in JavaScript projects and vice versa. TypeScript is a superset of JavaScript, which means that any valid JavaScript code is also valid TypeScript code.
NOTE : Whether we import Typescript code into JS, or Javascript code into TS we always perform compilation using TSC and only execute the compiled code.
Import Typescript into JS files
We can use TypeScript code in JavaScript projects by simply compiling the TypeScript code into JavaScript. The generated JavaScript code can be used in any JavaScript environment, and it will work the same way as regular JavaScript code. Importing TypeScript modules into JavaScript files does not require any special configuration other than compiling the TypeScript code into JavaScript.
Example] Below we import Typescript functions into JS file, the Typescript code will be compiled to JS with TSC and the generated JS is executed
Import Javascript into TS files
Importing regular Javascript modules inside a Typescript file require some configuration, since the JS modules don't have type information which can cause errors or warnings when you try to use it in your TypeScript code. Below are some approaches for importing JS modules into TypeScript files :
1] Enable the "allowJS" option
If you are working with JavaScript files in your TypeScript project and want to be able to import them directly, you can enable the 'allowJs' option in your tsconfig.json file. This allows the TypeScript compiler to include JavaScript files in the compilation process along with TypeScript files and perform type checking on them.
2] Use Type Declaration File
Most third-party packages come with their own type declaration files or if not we can install one from the DefinitelyTyped project. InCase a definition file is not available we can create one on our own.
NOTE : If we are importing our own JS modules into TS file, we must also set the "allowJS=true" to include those JS files into the compiled code.
---------------------------------------------------------------------------------------------------------------
Literal Types
In Typescript, a literal type is basically a type with specific literal value attached to it. Literal types can be used to enforce that a value must be of a specific type and a specific value.
Literal types can also be combined with union types to create more complex types. For example, a variable of type "Alice" | "Bob" can have the value "Alice" or "Bob", but not any other string value.
Example] Below we create literal types and use them with functions and variables.
KeyOf Operator
The keyof operator in TypeScript is a type operator that produces a union type of all the keys of a given type. It allows you to extract the keys of an object or a type, and use them as a type themselves.
The keyof operator in TypeScript is closely related to literal types, we can use it to create types which can only store values from the set of extracted keys, passing any other values will result in an compile-time error.
Example] Below extract keys from interfces & types to create new union types.
Template Literal Types
Template literal types in TypeScript are a way to manipulate string values as types. They allow you to create a type based on the result of string manipulation or concatenation. Template literal types are created using the backtick (``) character and string manipulation expressions within the type.
---------------------------------------------------------------------------------------------------------------
Conditional Types
In TypeScript, conditional types allow you to express types that depend on other types. They provide a way to create more flexible and dynamic types that can change based on the values of other types.
The only condition that we can check in a conditional type is whether a given type extends (or is a child of) some other type. In TypeScript, the 'extends' keyword is used to check whether one type is assignable to another type. This allows us to create conditional types that can assign different types based on whether a given type extends another type.
While the extends keyword is the only way to express a condition in a conditional type, it can be used in conjunction with other type operators such as union types, intersection types, and keyof types to create more complex and powerful type definitions.
---------------------------------------------------------------------------------------------------------------
Index Signatures
An index signature is basically a rule that if certain type of key exist then it can only hold a certain type of value. It allows you to define an object type that can have any number of properties with different types of keys and values, but if a key has an certain type, then it can only hold a certain type of value, as specified by the index signature. This makes it possible to create flexible object types that can still enforce type safety for their properties.
The syntax for an index signature in Typescript is the following :
NOTE : The 'keyType' in the index signature syntax is just a placeholder name, it can be any valid name. The 'keyType' is the type of the key, and 'valueType' is the type of the value associated with that key. The key type can be any primitive type or a union of primitive types, while the value type can be any valid TypeScript type, including other object types or interfaces.
Example] Below we enforce that keys of string type can only hold number or string type values, if we give any other type of value we'll get an error.
Example] Below we define index signatures and apply them to different objects.
NOTE : Index signatures work for interfaces and type aliases, but any object can only have one index signature.
Mapped Types
In TypeScript, mapped types are a powerful feature that allows you to transform an existing type into a new type by applying a transformation to all properties. Mapped types are based on a set of index signatures that define how the properties of the input type should be transformed to create the output type.
The Utility types like Partial, ReadOnly and various other types are based on mapped types, which allows them to map properties to various transformations.
Mapped types are commonly made to be of generic type so that they can work with a variety of different input types and are based on index signatures. In Mapped types, we iterate over all the keys of a given type or interface using the "in" operator, which is often used with "keyof" operator to extract a union of keys from given type.
Example] Below we define various mapped types to transform any given type.
Example] Below we define mapped type to transform all properties to boolean.
Example] Below we define mapped type with and without keyOf operator.
---------------------------------------------------------------------------------------------------------------
Typescript Namespaces
In TypeScript, a namespace is a way to organize code into a logical group or module. A namespace is declared using the namespace keyword, followed by the name of the namespace and a pair of curly braces {} that contain the code to be grouped together.
Namespaces can be used to avoid naming conflicts between different parts of a program, as all the code within a namespace is effectively encapsulated and isolated from other code.
Example] Below we define multiple namespaces and acess them in different file.
---------------------------------------------------------------------------------------------------------------
Typescript Decorators
TypeScript decorators are a feature introduced in TypeScript that allow you to add metadata and modify the behavior of classes, methods, properties, and parameters at runtime. They provide a way to extend and modify the functionality of existing code without directly modifying the code itself.
Decorators are executed during the class declaration phase, specifically when the class is defined. They are invoked immediately after the class declaration and before any instances of the class are created.
There are different types of Typescript decorators, some are as followed :
- Class Decorators : Applied to classes, they modify or extend class behavior or provide additional metadata.
- Method Decorators : Applied to methods, they modify or observe method behavior, provide metadata, or perform actions before or after method execution.
- Property Decorators : Applied to properties, they modify or observe property behavior, provide metadata, or perform actions related to the property.
- Parameter Decorators : Applied to method or constructor parameters, they modify or observe parameter behavior, provide metadata, or perform actions related to the parameter.
NOTE : Decorators are essentially functions that can be attached to classes, methods, properties, or parameters using the "@" symbol followed by the decorator name.
NOTE : Different types of decorator functions in TypeScript receive different types of arguments or inputs based on the element they are applied to.
Class decorators receive a single argument, which is the constructor function of the class being decorated. The type of this argument is Function or typeof ClassName , whereas Method decorators receive three different arguments.
---------------------------------------------------------------------------------------------------------------

Comments
Post a Comment