Express.d.ts

0 views
Skip to first unread message

Fe Kittner

unread,
Aug 5, 2024, 12:53:45 PM8/5/24
to distvacorrnozz
TypeScriptoffers multiple ways to represent objects in your code, one of which is using interfaces. Interfaces in TypeScript have two usage scenarios: you can create a contract that classes must follow, such as the members that those classes must implement, and you can also represent types in your application, just like the normal type declaration. (For more about types, check out How to Use Basic Types in TypeScript and How to Create Custom Types in TypeScript.)

You may notice that interfaces and types share a similar set of features; in fact, one can almost always replace the other. The main difference is that interfaces may have more than one declaration for the same interface, which TypeScript will merge, while types can only be declared once. You can also use types to create aliases of primitive types (such as string and boolean), which interfaces cannot do.


Interfaces in TypeScript are a powerful way to represent type structures. They allow you to make the usage of those structures type-safe and document them simultaneously, directly improving the developer experience.


In this tutorial, you will create interfaces in TypeScript, learn how to use them, and understand the differences between normal types and interfaces. You will try out different code samples, which you can follow in your own TypeScript environment or the TypeScript Playground, an online environment that allows you to write TypeScript directly in the browser.


You will need sufficient knowledge of JavaScript, especially ES6+ syntax, such as destructuring, rest operators, and imports/exports. If you need more information on these topics, reading our How To Code in JavaScript series is recommended.


This tutorial will reference aspects of text editors that support TypeScript and show in-line errors. This is not necessary to use TypeScript but does take more advantage of TypeScript features. To gain the benefit of these, you can use a text editor like Visual Studio Code, which has full support for TypeScript out of the box. You can also try out these benefits in the TypeScript Playground.


Since values must follow what is declared in the interface, adding extraneous fields will cause a compilation error. For example, in the object literal, try adding a new property that is missing from the interface:


When creating interfaces, you can extend from different object types, allowing your interfaces to include all the type information from the extended types. This enables you to write small interfaces with a common set of fields and use them as building blocks to create new interfaces.


You could then create a new interface that extends from it, inheriting all its fields. In the following example, the interface Logger is extending from the Clearable interface. Notice the highlighted lines:


The Logger interface now also has a clear member, which is a function that accepts no parameters and returns void. This new member is inherited from the Clearable interface. It is the same as if we did this:


Returning to the Clearable example used previously, imagine that your application needs a different interface, such as the following StringList interface, to represent a data structure that holds multiple strings:


By making this new StringList interface extend the existing Clearable interface, you are specifying that this interface also has the members set in the Clearable interface, adding the clear property to the type definition of the StringList interface:


Notice that the callable signature resembles the type declaration of an anonymous function, but in the return type you are using : instead of =>. This means that any value bound to the Logger interface type can be called directly as a function.


A nice feature of setting variables to have a specific type, in this case setting the logger variable to have the type of the Logger interface, is that TypeScript can now infer the type of the parameters of both the logger function and the function in the log property.


As shown in the previous sections, the interface declaration can be used to represent a variety of objects, from functions to complex objects with an unlimited number of properties. This is also possible with type declarations, even extending from other types, as you can intersect multiple types together using the intersection operator &.


One of the features available only for the interface declaration is declaration merging, which you will learn about in the next section. It is important to note that declaration merging may be useful if you are writing a library and want to give the library users the power to extend the types provided by the library, as this is not possible with type declarations.


TypeScript can merge multiple declarations into a single one, enabling you to write multiple declarations for the same data structure and having them bundled together by the TypeScript Compiler during compilation as if they were a single type. In this section, you will see how this works and why it is helpful when using interfaces.


When the TypeScript Compiler starts reading your code, it will merge all declarations of the DatabaseOptions interface into a single one. From the TypeScript Compiler point of view, DatabaseOptions is now:


Declaration merging is helpful when you need to augment existing modules with new properties. One use-case for that is when you are adding more fields to a data structure provided by a library. This is relatively common with the Node.js library called express, which allows you to create HTTP servers.


When working with express, a Request and a Response object are passed to your request handlers (functions responsible for providing a response to a HTTP request). The Request object is commonly used to store data specific to a particular request. For example, you could use it to store the logged user that made the initial HTTP request:


Here, the request handler sends back to the client a json with the user field set to the logged user. The logged user is added to the request object in another place in the code, using an express middleware responsible for user authentication.


If you check the type of the Request object in the express type declaration, you will notice that it is an interface added inside a global namespace called Express, as shown in documentation from the DefinitelyTyped repository:


Note: Type declaration files are files that only contain type information. The DefinitelyTyped repository is the official repository to submit type declarations for packages that do not have one. The @types/ packages available on npm are published from this repository.


To use module augmentation to add a new property to the Request interface, you have to replicate the same structure in a local type declaration file. For example, imagine that you created a file named express.d.ts like the following one and then added it to the types option of your tsconfig.json:


From the TypeScript Compiler point of view, the Request interface has a user property, with their type set to an object having a single property called name of type string. This happens because all the declarations for the same interface are merged.


Suppose you are creating a library and want to give the users of your library the option to augment the types provided by your own library, like you did above with express. In that case, you are required to export interfaces from your library, as normal type declarations do not support module augmentation.


In this tutorial, you have written multiple TypeScript interfaces to represent various data structures, discovered how you can use different interfaces together as building blocks to create powerful types, and learned about the differences between normal type declarations and interfaces. You can now start writing interfaces for data structures in your codebase, allowing you to have type-safe code as well as documentation.


Tengo preparado mi archivo d.ts, todo pareca ir bien, pero lo gracioso viene cuando cierro el documento express.d.ts, al hacerlo el linter indica que tengo errores justo en los tipos que he declarado. Es como si no lo tomara en cuenta.

3a8082e126
Reply all
Reply to author
Forward
0 new messages