David Siegel David Siegel - 1 year ago 126
TypeScript Question

How do I configure a TypeScript project that uses JavaScript modules compiled from PureScript?

TL;DR I want to create TypeScript typings for compiled PureScript modules, and distribute them in my npm package. I'm more than happy to maintain these typings manually, I just can't figure out what I need to put in tsconfig.json (up and downstream) and package.json.




I have a project where the core functionality is implemented in PureScript with a TypeScript CLI, and all of this is ultimately distributed as JavaScript via npm. I've created a similar project with a simpler layout:

.
├── cli # TypeScript CLI project
│   ├── main.ts
│   └── tsconfig.json

├── output # Compiled PureScript modules
│   └── People
│   ├── externs.json
│   └── index.js

├── src # PureScript source
│   └── People.purs

└── types # TypeScript types for PureScript modules
└── People.d.ts


In
src/People.purs
I define the core functionality:

module People where

type Person = { name :: String }

david :: Person
david = { name: "David "}


In
types/People.d.ts
I define TypeScript types for PureScript modules so I can safely use them from TypeScript:

declare module "People" {
export interface Person {
name: string;
}

export const david: Person;
}


Finally, in
cli/main.ts
, I want to import the compiled PureScript modules with the TypeScript types I've defined:

import * as People from "People";

console.log(People.david);


I can get this to build, but when I try to run the ultimate JavaScript (
node cli/main.js
), the
require
calls generated by
tsc
fail because they don't use absolute paths--
require("People")
should be
require("./People")
in this case. Making the
import
statements in TypeScript relative disassociates the typings.

I'm having a hard time meeting these goals:


  • Maintain TypeScript typings for all PureScript modules imported from TypeScript.

  • Ensure that TypeScript typechecks.

  • Ensure that
    require
    calls for PureScript modules resolve at runtime.

  • Distribute TypeScript typings with npm package, so that TypeScript consumers can use these typings as well.



If you have experience requiring PureScript modules from TypeScript, and distributing all of this via npm, I would really appreciate some guidance!

Answer Source

Declaring namespaces rather than modules makes everything work. For example, in types/People.d.ts:

declare namespace People {
    export interface Person {
        name: string;
    }

    export const david: Person;
}

export = People;
export as namespace People;

After compiling PureScript modules and before compiling TypeScript, I cp types/People.d.ts output/People/index.d.ts. This makes the TypeScript code happy with the same absolute imports (e.g. import * as People from "People";), and TypeScript libraries also see these types without additional configuration.

A few issues remain, though:

  • I still have to relativize the PureScript module imports in the compiled TypeScript (by prepending ../ in a post-build step); consumers of my library don't have to do this, though, since it goes through npm and that magically makes it work.
  • I can't figure out how to export namespaces with periods, so modules like Data.Maybe are represented by namespaces like Data_Maybe.
  • I don't know much about namespaces vs. modules, so there may be other caveats.
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download