October 15, 2016

Learn how to create your own type definition files and how to contribute to the TypeScript community at DefinitelyTyped #

Helping your community is AWESOME #

It is Saturday evening and it’s raining a lot in Ireland. So I decided to spend some time on Twitter looking for interesting TypeScript news. Over the last year I’ve been using the @DubTypeScript account to share TypeScript news. This is the official account of the Dublin TypeScript Meetup which is based in Dublin (Ireland) and is organized by myself.

I was searching for interesting tweets when I found the following one:

When I saw this tweet I was automatically thrilled because it is AWESOME when you find an opportunity to help the community like this.

I really think it is sad that only a small percentage of all the software engineers out there contribute to open source. Contributing to open source is not easy for newcomers but I have seen a lot of initiatives to try to improve this over the past year. Initiatives among which I would like to highlight the Hoodie efforts:

Hoodie aims to be one of open source’s most diverse and inclusive communities - opensource.com

And the “Your First PR” initiative:

Your First PR helps you get started contributing to Open Source by showcasing great starter issues on Github and elsewhere. - Your First PR

You may not believe me until you try it yourself but working on open source will make you a happier person. You might be working on an awesome project at work, in an amazing company with an amazing salary but nothing will fulfil you as much as helping others. Helping others will give you a sense of purpose that no salary or pension will ever be able to give you.

Working on open source has also helped me a lot in my professional career. It has allowed me to gain access to amazing opportunities like writing a technical book.

Or having the honor of being awarded with the Microsoft Most Valuable Professional (MVP) award:

In summary, contributing to open source will not only make you a happier person. It will also help you to become a better professional.

In this article I’m going to explain how to create type definitions for an open source npm module. Please note that I’m assuming that you are familiar with TypeScript, Git and npm but you don’t need to be familiar with GitHub or React.

Writing your own type definitions files #

To create our own type definitions, we need to see the source code of the npm module for which we are trying to create type definitions.

In this post we are working on the type definitions for the react-side-effect npm module. It is easy to find the source code doing a quick search on Google but, the way I do it, is by heading to the module page on npmjs.com.

Once you find the module on npmjs.com, you will be able to find a link to its GitHub repository on the right-hand side of the page:

Follow the link to GitHub and try to find the package.json file using the file explorer. We then need find the main field in the package.json file:

In 99% percent of the libraries, the main file is the /src/index.js file but, if that is not the case, the package.json file can help you to identify the main file.

Head to the main file to see its source code. In this case, the main file can be found here.

Wait a second, I just noticed something. This source code was created by Dan Abramov! This means that we are about to be able to learn from the source code one of the most popular software developers within the React and the JavaScript community. This is an example of the kind of amazing opportunities that you will encounter if you work on open source. You will be able to learn from some of the best software engineers in the world and even get to know them for free!

Once you find the source code, the first thing that we need to do, is to identify if this library was created using ES5 or ES6. We can usually identify the version of JavaScript being used by looking at the module imports. Then try identify which modules are imported (dependencies) and which elements are exported (public interface):

It looks like this module depends on 2 other modules:

Component is imported from react

is imported from ExecutionEnvironment and shallowEqual is imported from fbjs

When I search for the ExecutionEnvironment and shallowEqual variables in the source code, I can see that ExecutionEnvironment and shallowEqual are used internally and are not exported by react-side-effect .

The module exports a function named withSideEffect , which the function takes 3 arguments named:

reducePropsToState

handleStateChangeOnClient

mapStateOnServer

The library is written in JavaScript so we don’t know the types of these 3 arguments.

At this point we can start creating our type definition file:

declare module "react-side-effect" { function withSideEffect( reducePropsToState: any, handleStateChangeOnClient: any, mapStateOnServer: any ): any; export = withSideEffect; }

I can also see that the withSideEffect function returns another function named wrap , which takes an argument named WrappedComponent . Once more we don’t know the type of this argument but by looking at its name and a typeof check we can guess that it is a React Component.

The Warp function returns a React component named SideEffect .

.

The last thing that we need to find out is the types of the arguments. This is the most complicated part. In this case I’m lucky because I’m familiar with React and the library is well documented.

The handleStateChangeOnClient and mapStateOnServer arguments are functions that expect an object state as its only argument.

Because I’m familiar with React I know that the state can take any from so I used the type any .

Later I noticed that reducePropsToState actually takes an array as argument:

At this point I updated the type definitions:

declare module "react-side-effect" { import React = __React; function withSideEffect( reducePropsToState: (propsList: any[]) => any, handleStateChangeOnClient: (state: any) => any, mapStateOnServer: (state: any) => any ): (WrappedComponent: React.Component<any, any>) => React.Component<any, any>; export = withSideEffect; }

Note that I have used export = because that is what I can see in the library’s source code. Sometimes you will see export default x or export { x } you need to ensure that your type definitions file use the same kind of export that is used in the library.

Now that we have the initial type definitions ready, we are going to try to add them to DefinitelyTyped.

Contributing to DefinitelyTyped on GitHub #

To contribute to DefinitelyTyped you need to visit the official repository at https://github.com/DefinitelyTyped/DefinitelyTyped.

Once you are in the page you need to fork the repository (you will need to be logged in). You can fork the repository by clicking on the top right “fork” icon:

After forking the repository, you will be redirected to the page of your fork. You need to find the HTTPS URL to clone your fork:

Once you have the clone URL, you can clone the repository using the terminal or console of your operating system.

$ git clone https://github.com/YOUR_USER_NAME/DefinitelyTyped.git

When the repository has been cloned you need to move into the newly created folder:

$ cd DefinitelyTyped

We are going to add a new type definition file so we can say that we are going to work on a new feature. We are going to create a new branch:

$ git checkout -b react-side-effect

We need to install the dependencies of this project:

$ npm install

We can then create a new folder:

$ mkdir react-side-effect

A new file for our type definitions:

$ touch react-side-effect/react-side-effect.d.ts

And a new file for the test of the type definitions:

$ touch react-side-effect/react-side-effect-tests.ts

We can copy our initial type definitions into the react-side-effect.d.ts file that we just created. I copied it and added some comments required by DefinitelyTyped:

// Type definitions for react-side-effect v1.0.2 // Project: https://github.com/gaearon/react-side-effect // Definitions by: Remo H. Jansen <https://github.com/remojansen/> // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped /// <reference path="../react/react.d.ts"/> declare module "react-side-effect" { import React = __React; function withSideEffect( reducePropsToState: (propsList: any[]) => any, handleStateChangeOnClient: (state: any) => any, mapStateOnServer: (state: any) => any ): (WrappedComponent: React.Component<any, any>) => React.Component<any, any>; export = withSideEffect; }

To create the test, I just to copied one of the examples from the react-side-effect documentation. The example is written in JavaScript but can be migrated to TypeScript with ease:

/// <reference path="react-side-effect.d.ts" /> import * as React from "react"; import * as withSideEffect from "react-side-effect"; interface DocumentTitleProps { title: string } class DocumentTitle extends React.Component<DocumentTitleProps, any> { public render() { if (this.props.children) { return React.Children.only(this.props.children); } else { return null; } } } function reducePropsToState(propsList: any[]) { var innermostProps = propsList[propsList.length - 1]; if (innermostProps) { return innermostProps.title; } } function handleStateChangeOnClient(title: string) { document.title = title || ""; } let DocumentTitleWithSideEffects = withSideEffect( reducePropsToState, handleStateChangeOnClient )(DocumentTitle); export default DocumentTitleWithSideEffects;

If we search in the DefinitelyTyped repository documents, we will be able to find the instructions to test our changes before sending a pull request:

$ npm test

When I executed the tests I encountered two issues:

import * as withSideEffect from "react-side-effect";

Was throwing:

[ts] Module ‘“react-side-effect”’ resolves to a non-module entity and cannot be imported using this construct.

I was able to fix it adding a namespace . I found the solution after a quick search online:

declare module "react-side-effect" { import React = __React; function withSideEffect( reducePropsToState: (propsList: any[]) => any, handleStateChangeOnClient: (state: any) => any, mapStateOnServer: (state: any) => any ): (WrappedComponent: React.Component<any, any>) => React.Component<any, any>; namespace withSideEffect {} // https://github.com/Microsoft/TypeScript/issues/5073 export = withSideEffect; }

The second problem was that:

let DocumentTitleWithSideEffects = withSideEffect( reducePropsToState, handleStateChangeOnClient )(DocumentTitle);

Was throwing:

[ts] Argument of type ‘typeof DocumentTitle’ is not assignable to parameter of type ‘Component’.

Property ‘setState’ is missing in type ‘typeof DocumentTitle’.

I was able to fix this issue by going through the type definitions of other similar react libraries. I found the solution in the redux-forms type definitions:

// Type definitions for react-side-effect v1.0.2 // Project: https://github.com/gaearon/react-side-effect // Definitions by: Remo H. Jansen <https://github.com/remojansen/> // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped /// <reference path="../react/react.d.ts"/> declare module "react-side-effect" { import React = __React; function withSideEffect( reducePropsToState: (propsList: any[]) => any, handleStateChangeOnClient: (state: any) => any, mapStateOnServer?: (state: any) => any ): ClassDecorator; class ElementClass extends React.Component<any, any> {} interface ClassDecorator { <T extends (typeof ElementClass)>(component:T): T; } namespace withSideEffect {} // https://github.com/Microsoft/TypeScript/issues/5073 export = withSideEffect; }

We are ready to send our changes to our fork. Let’s add the new folder to source control:

$ git add react-side-effect

Then check which files are ready to be committed:

$ git status

The files seem to be correct:

new file: react-side-effect/react-side-effect-tests.ts new file: react-side-effect/react-side-effect.d.ts

We can now commit the changes:

$ git commit -m "Added type definitions for react-side-effect"

To finish, let’s push the changes to a new branch in our fork:

$ git push origin react-side-effect

If you head to the page of your fork on GitHub you will see a notice box which allows you to create a pull request (PR) for your changes:

After creating the PR a the DefinitelyTyped continuous integration process will be triggered. The CI process will run the tests just like you have done in your local machine so it should be fine.

This time the CI process has been successful ??

If your PR fails during the CI process, you will need to check the Travis CI logs. You can access the logs by clicking on “Show all checks” and then clicking on “Details”. If you need help you will be abel to use the comments section in the PR and the DefinitelyTyped time will try their best to help you.

We have learned how to create our own type definition files and how to share them with the open source community.

There is a process that you can follow:

Identify imports and exports of a given module. Identify types of the exported function (arguments and return). Create initial .d.ts file. Create test using a example from the module docs. Run test locally and fix issues. Send PR to DefinitelyTyped.

Creating type definitions files is not a trivial task but it is a positive experience:

We get to review code created by people like Dan Abramov.

We learn how to solve issues like #5073.

We learn about new libraries.

We will make at least one person happy.

You can find more resources about the creation of type definitions at www.typescriptlang.org and definitelytyped.org.

Please feel free to share thoughts about this article with us via @WolkSoftwareLtd.

Don’t forget to subscribe if you don’t want to miss it out future articles!

866 Kudos