Deep and Composed

Learn via video courses
Topics Covered

Overview

Different sorts of values are supported by the TypeScript language. It offers data types for JavaScript, allowing it to be transformed into a strongly typed programming language. Although JavaScript does not allow data types, we may use the data types functionality in JavaScript with the aid of TypeScript. When an object-oriented programmer wishes to use the type feature in any scripting language or object-oriented programming language, TypeScript comes in handy. Before the application uses the specified values, the Type System validates them. It guarantees that the code works as it should.

Introduction

To make your JavaScript TypeScript, TypeScript's type system is intended to be optional. You may gradually switch from JavaScript to TypeScript since TypeScript does not prevent JavaScript from emitting in the presence of Type Errors.

Let's now begin using the TypeScript type system's syntax. In this approach, you may use these annotations in your code right away and observe their advantages. This will get you ready for a later, deeper dig.

Primary Annotations

As previously indicated, types are annotated using the syntax. A Type Annotation can be any object that is present in the type declaration space. Type annotations for variables, function parameters, and function return values are seen in the following example:

Primitive Types

The TypeScript type system does a good job of emulating JavaScript basic types. This refers to the following examples of string, number, and boolean:

Arrays Arrays have their type syntax in TypeScript, which makes it simpler for you to annotate and explain your code. Basically, the syntax is postfixing [] to any legitimate type annotation (e.g. :boolean[]). It safeguards you against mistakes like assigning a member of the incorrect type and enables you to securely do any array operation that you would typically perform. The following illustrates this:

Interfaces The main method in TypeScript for combining numerous type annotations into a single named annotation is through interfaces. Think about the following instance:

Here, the annotations have been written first: Second added to the string creates a new annotation. A person who makes sure each member is type-checked. We will devote a full section to explaining how you can take advantage of TypeScript's powerful interfaces.

Special Types

There are a few kinds in TypeScript that are special in addition to the primitive types that have already been discussed. Any, Null, Undefined, and Void are these.

Any In the TypeScript type system, any type has a particular location. It provides a way for you to tell the compiler to leave the type system alone. Any type in the type system is compatible with any other type. It may therefore be assigned to anything, and anything can be assigned to it. The following example exemplifies this:

You will first be close friends with any if you are converting JavaScript code to TypeScript. It is up to you to safeguard the type's safety, so don't take this friendship too seriously. Essentially, you are instructing the compiler to perform no useful static analysis.

Null and Undefined Depending on the strictNullChecks compiler setting, the type system will handle them in a certain way (we cover this flag later). The type system basically treats the null and undefined JavaScript literals as being of type any when strictNullCheck:false is set. Any other type may be given to these literals. The example below exemplifies this:

Void The void type implies the lack of any type at all. It is similar to the inverse of any type.

The void type is typically used as the return type of functions that do not return a value. As an example:

It is best to use the void type as the return type of a function or method that does not return any value. You can receive the following advantages by doing so:

Improve code clarity: you don't have to read the entire function body to determine if it returns anything.Make certain that the function with the void return type is never assigned to a variable. It's worth noting that when you use the void type for a variable, you could only assign undefined to it. In this scenario, the void type is ineffective. As an example:

If the —strictNullChecks option is not given, the useless can be set to null.

Generics In computer science, a lot of algorithms and data structures are not type-dependent. You still need to apply a restriction between different variables, though. A basic example of a toy is a function that accepts a list of objects and returns a list of items that have been reversed. The restriction in this case is between the parameters supplied to the function and the results it returns.

Union Type In JavaScript, it's rather typical to wish to enable a property to be one of several kinds, such as a string or a number. The union type, marked by the symbol | in a type annotation, such as string|number, is useful in this situation. A function that accepts a single object or an array of objects is a frequent use case. For example:

Intersection Type In JavaScript, the intersection type extend pattern is frequently used to combine the features of two existing objects into a single new object. You may utilise this pattern safely by using an intersection type, as seen below:

What are Deep Dive and Composed Types in TS?

It might be challenging to organise modules in a way that gives your desired API form. For instance, we could want a module that can be called with or without new to create various kinds, has several named types accessible in a hierarchy, and also has certain attributes on the module object.

By comprehending a few fundamental TypeScript notions, you can produce any sort of declaration with complete confidence.

Types

If you're reading this article, you presumably have a general understanding of what a TypeScript type is. However, in order to be clearer, a type is introduced with:

  • A declaration of a type alias (type sample = number | string;)
  • A definition of an interface (interface I x: number[]; )
  • A class declaration for the ( class C { } )
  • A declaration for an enum (enum E {X, Y, Z} )
  • an import statement that mentions a kind

These declaration formats each produce a fresh type name.

Values

Similar to types, you likely already know what a value is. Expressions that include values can refer to runtime names. Let x, for instance, x= 10. This generates the value x.

To be clear once more, the following factors influence values:

  • var, let, and const declarations
  • A declaration of a namespace or module that includes a value
  • Enum declaration
  • A declaration of class
  • An import statement that mentions a value
  • A declaration of a function

Namespaces

Namespaces can contain types. We may declare that the type Z originates from the X.Y namespace, for instance, if the declaration let p: X.Y.Z is present.

This small but significant distinction states that X.Y is not always a type or a value.

Simple Combinations: One name, Multiple Meanings

Given the name HP, we may find up to three possible interpretations for HP: a type, a value or a namespace. The term "electronic commerce" refers to the sale of electronic goods. For example, in the expression let p: HP. HP = HP;, HP is used first as a namespace, then as a type name, and then as a value. These interpretations might end up referring to completely distinct declarations!

This may appear perplexing, but it's really extremely convenient as long as we don't unduly overburden things. Let's have a look at some of the most beneficial characteristics of this merging behaviour.

Built-in Combinations

For example, observant readers will observe that class appeared in both the type and value lists. The declaration class A generates two objects: a type A that refers to the class's instance shape and a value A that references the class's function Object() function. The answer is yes.

User Combinations

Assume we created a module file.

This works well enough, but we can suppose if Some and the sample were extremely closely connected such that you'd prefer them to have the same name. Using combining, we may present these two distinct objects (the value and the type) under the same name value:

Advanced Combinations

Some declarations can be merged into several declarations. Class C{} and interface C{}, for example, can coexist and both contribute attributes to the C types.

This is permissible as long as there is no disagreement. Values will always clash with other values of the same name unless they are specified as namespaces, types will conflict if they are declared with a type alias declaration (type s = string), and namespaces will never conflict.

Let's see how this can be put to use.

Adding Using an Interface

With another interface declaration, we may add more members to an interface:

This also applies to classes:

Keep in mind that utilising an interface will not allow us to add to type aliases (type s = string;).

Adding Using a Namespace

Any method that avoids causing a conflict may be used to add new types, variables, and namespaces using a namespace declaration.

To illustrate, consider adding a static member to a class:

Keep in mind that we assigned a value to the static side of C in this example (its function Object() function). This is because we introduced a value, and another value serves as the container for all values (types are contained by namespaces, and namespaces are contained by other namespaces).

We could also give a class a namespaced type:

In this illustration, the namespace C didn't exist until we created the namespace declaration for it. The value and type meanings of C produced by the class do not conflict with the meaning of C as a namespace.

Finally, namespace declarations allowed us to do a wide variety of merges. Although not especially realistic, the following example demonstrates a variety of intriguing behaviour:

Explanation In this illustration, the first block generates the names with the following meanings: A number n (because the namespace declaration contains a value, x), An n namespace (because the namespace declaration contains a type, m), an m type within the n namespace, The n namespace's type x (the instance shape of the class), a characteristic of the n value with the value x (the function Object() function of the class).

The following name meanings are produced from the second block with a number-type value m that is a characteristic of the value n, x in a namespace, a characteristic of the n value with the value x, a type C in the namespace n.x, a property of the n.x value with the value C and A type n.

Composed Types

Composition is a fundamental notion in functional programming in which numerous functions are merged into a single function that performs all of the duties. One function's output gets fed into another's input. Many libraries, such as Lodash and Ramda, have a compose method to assist with this, and it is also a popular pattern in React. These helpers enable you to do the following:

into this

The newly created function can then be utilised as required. Compose's counterpart is a pipe, which is simply composed with the parameters reversed. In this scenario, the arguments are presented in the order in which they are performed, rather than the order in which they appear when nested. In the above compose example, the inner fxn4 function is run first, and the output is then sent to fxn3, fxn2, and fxn1. In many cases, it is more intuitive to conceive of the functions as a Unix pipe, with the value handedintoo the left and piped from one function to the next. As an illustration, the shell command to discover the biggest files in a directory would be:

Consider the following in a hypothetical JavaScript environment:

You may do that using pa ipe like follows:

Consider importing a JSON file, doing multiple actions on it, and then saving it as a more realistic example.

Typescript Compose Function

The word 'composing functions' is rather prevalent in functional programming. Let's start with native javascript and then add types one by one to inspect and validate it.

Step 1: Assume we have three functions named Name, Length, and Even. These are basically three javascript functions defined as follows:

This is how we can put them to use:

Output:

Step 2: Let's combine these functions:

That does not seem good; we have a lot of parentheses and it is also tough to understand. Can we put these functions together nicely? Yes..

Let's start with a general compose function.

This is a higher-order function that takes an array of functions and returns a function that takes an object and returns the result of executing these functions (from right to left) on that object when called.

Step 3: Let's rebuild the previous three functions in Typescript to improve the type checks.

Step 4: To improve type checks, let's construct the generic compose function in typescript.

Because a composable function has arity 1 (accepts one parameter), I can define a generic type for these functions as follows:

Now we can write the remainder parameter of the compose function as follows:

Step 5: We require the following helper types to represent the following

  • This function's argument type Name must be used as a type for the ComposedFn argument. We may make advantage of built-in parameters.
  • This function's return type Even to utilise it as a type for ComposedFn return type. We may make advantage of the built-in ReturnType.
  • The final element of a typescript tuple.

Conclusion

  • Use the void type as the return type for functions that return nothing.
  • Union types are helpful for simulating scenarios in which values' types might overlap.
  • The most intriguing aspect of TypeScript is without a doubt its expressive type system, which enables it to fulfil its promise of enabling the creation of scalable JavaScript.
  • By giving the TypeScript compiler extra type information through type annotations, you may facilitate the development process and create more resilient and maintainable programmes.
  • You have access to a wide range of type annotations, including those for classes, arrays, arbitrary objects, tuples, literal types, interfces, and more. Primitive types like iintegersand text are among them.
  • The compose function can combine functions that return the same types as expected.