Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto generate index.d.ts, type definitions, from a typescript module

If I have a TypeScript module saved as my-function.ts as follows :

export function myFunction (param: number): number { return param }

This will be compiled to JavaScript in whichever way and loose its type definitions. I am then able to create a index.d.ts file which declare this module's definitions, but this seems a bit tedious to redefine/redeclare the definitions.

Are there ways to generate the type definitions automatically from the my-function.ts file to a index.d.ts file?

like image 629
altus Avatar asked Jun 05 '17 08:06

altus


People also ask

What is index D ts in TypeScript?

*. d. ts files are used to provide typescript type information about a module that's written in JavaScript, for example, underscore / lodash / aws-sdk. This will allow you to use the javascript modules without the need to convert them to ts without getting any type of error on your code.

What does ts mean in TypeScript?

ts" file is used to provide typescript type information about an API that's written in JavaScript. The idea is that you're using something like jQuery or underscore, an existing javascript library. You want to consume those from your typescript code.

What is declaration D ts?

Declaration files, if you're not familiar, are just files that describe the shape of an existing JavaScript codebase to TypeScript. By using declaration files (also called . d. ts files), you can avoid misusing libraries and get things like completions in your editor.


2 Answers

If you compile with the --declaration flag, TypeScript will automatically generate .d.ts files for you.

This mode will require that you certain types are visible so that they can be described in your .d.ts files.

like image 99
Daniel Rosenwasser Avatar answered Oct 10 '22 23:10

Daniel Rosenwasser


Here's how I managed to solve it:

Creating the infra

  1. Create an new Node package with typescript for the infra.
  2. Inside the new package, make sure to configure a tsconfig.json with declaration:true Doing so will cause typescript to generate definition files which can be consumed by the users of this infra.

My tsconfig.json:

{
"compilerOptions": {
   "target": "es5",
   "module": "commonjs",
   "declaration": true,
   "outDir": "./tsOutputs"
 },
"include": [
  "lib/**/*.ts",
  "index.ts"
],
"exclude": [
  "test/**/*.ts"
]
  }
  1. Create a an "index.ts" file which will export the public API of the infra.

Note: In order to be able to cast & create instances of objects, you need to have two different exports per each entity. Once as type and another as const.

Here's my index.ts:

import {HttpClient as HC} from "./lib/http/http-client";

import {HttpRequest as HReq, HttpResponse as HRes}  from "./lib/http/contracts";

export namespace MyJsInfra

{

export type HttpClient = HC;

   export namespace Entities{
       export type HttpRequest = HReq;
       export const HttpRequest = HReq;

       export type HttpResponse = HRes;
       export const HttpResponse = HRes;
   }
}`

You can read more info on the reasoning behind this dual declaration in here: https://github.com/Microsoft/TypeScript/issues/10058#issuecomment-236458961

  1. After all of the following, when we'll run build we should have the corresponding "*.d.ts" files per each type. Now we have to handle the package.json of the infra, in order to pack all the items.

  2. Inside the package.json make sure to set the types, main to point to the generated index.d.ts & index.js files. In addition, you have to make sure that the "*.d.ts" files are being packaged as part of infra. In my case, I've specified the following pattern in the files property: "tsOutputs/**/*.d.ts"

Here's my package.json:

    {
  "name": "my-js-infra",
  "version": "1.0.0",
  "description": "Infrastructure code.",
  "scripts": {
    "build":"./node_modules/.bin/tsc -p .",
    "prepublish":"npm run build",
  },

 "homepage": "https://github.com/Nadav/My.JS.Infra#readme",
  "devDependencies": {
   ...
    "typescript": "^2.4.2",
   ...
  },
  "dependencies": {
     ...
    "needle": "^1.4.2",
     ...
  },
  "files": [
    "tsOutputs/**/*.js",
    "tsOutputs/**/*.d.ts",
    "tsOutputs/index.d.ts"
  ],
  "types":"tsOutputs/index.d.ts",
  "main":"tsOutputs/index.js"
}

All done. Now you can publish your common code.


Consuming the code

  1. Install the infra. In our case the user have to use: npm install my-js-infra --save
  2. Modify the tsconfig.json of the consuming application to load the modules using the Node module resolution. You do so by setting moduleResolution:true inside the file.

Here's my tsconfig.json:

{
    "compilerOptions": {
        "target": "es5",
        "lib": ["es5", "es6"],
        "module": "umd",
        "sourceMap": true,
        "watch": false,
        "outDir": "./tsOutputs",
        "moduleResolution":"node" /* This must be specified in order for typescript to find the my-js-infra. Another option is to use "paths" and "baseUrl". Something like:
                                        ...
                                        "baseUrl": ".", // This must be specified if "paths" is used.
                                        "paths":{
                                            "my-js-infra":["node_modules/my-js-infra/tsOutputs/index.d.ts"]
                                        }
                                        ...
                                    */
    }
}

You can read more on module resolution in Typescript in here: https://www.typescriptlang.org/docs/handbook/module-resolution.html

  1. Start using the code. For example:

import {MyJsInfra } from "my-js-infra";

public doMagic(cmd, callback) {
        try {
            var request:MyJsInfra.Entities.HttpRequest = {
                verb: "GET",
                url: "http://www.google.com",
            };

            var client = new MyJsInfra.HttpClient();
            client.doRequest(request, (err, data)=>{
                if (err)
                    return callback(err, null)
                return callback(null, data);
            })
        } catch (err) {
            callback(err);
        }
}
like image 12
nadavy Avatar answered Oct 11 '22 00:10

nadavy