如何配置使用从PureScript编译的JavaScript模块的TypeScript项目?

时间:2017-09-30 18:02:11

标签: node.js typescript npm purescript

TL; DR 我想为已编译的PureScript模块创建TypeScript类型,并将它们分发到我的npm包中。我非常乐意手动维护这些类型,我只是无法弄清楚我需要放入tsconfig.json(上行和下行)以及package.json。

我有一个project,其核心功能是使用TypeScript CLI在PureScript中实现的,所有这些最终都是通过npm以JavaScript的形式发布的。我创建了一个similar project,其布局更简单:

.
├── 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

src/People.purs中我定义了核心功能:

module People where

type Person = { name :: String }

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

types/People.d.ts中我为PureScript模块定义了TypeScript类型,因此我可以安全地从TypeScript中使用它们:

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

    export const david: Person;
}

最后,在cli/main.ts中,我想要使用我定义的TypeScript类型导入已编译的PureScript模块:

import * as People from "People";

console.log(People.david);

我可以将其构建,但是当我尝试运行最终的JavaScript(node cli/main.js)时,require生成的tsc次调用失败,因为它们不能使用在这种情况下,绝对路径 - require("People")应为require("./People")。在TypeScript中使import语句相对取消了对打字的关联。

我很难实现这些目标:

  • 维护从TypeScript导入的所有PureScript模块的TypeScript类型。
  • 确保TypeScript类型检查。
  • 确保require调用PureScript模块在运行时解析。
  • 使用npm包分发TypeScript类型,以便TypeScript使用者也可以使用这些类型。

如果您有从TypeScript需要PureScript模块的经验,并通过npm分发所有这些,我真的很感激一些指导!

3 个答案:

答案 0 :(得分:1)

您的Purescript模块被称为" Person",但应该是" People"。我认为通过相对路径导入,你可能会部分地进入,但我不认为在d.ts文件中存在相对路径输入这样的事情。

我检查了你的项目并重命名了模块,然后我将allowJs设置为true并输入了内联模块:

import * as OutputPeople from "../output/People";

type Person = {
  name: string;
};

type PeopleModule = {
  david: Person
}

const People: PeopleModule = OutputPeople

console.log(People.david);

我将修改放在这里的分支上:https://github.com/justinwoo/ps-js-ts-interop/commit/fc7c1239f9d1bac1cf2bea35f7a07d30c46a5f64

答案 1 :(得分:1)

如果你肯定需要使用绝对路径来要求你的PureScript包(例如import * as People from 'People'),我认为你需要破解节点模块分辨率算法(将你的PureScript输出放在{{1}中例如,node_modules的子目录。

但是,如果这不重要,并且您对相对导入(例如./output)没有问题,您可以执行以下操作:

  1. 使用PureScript模块的相对导入:

    CLI / main.ts:

    import * as People from './People'
  2. 将PureScript类型与TypeScript源并排放置:

    import * as People from './People';
    
  3. 将PureScript类型更改为本地模块声明而不是全局:

    CLI /人/ index.d.ts:

    .
    ├── People
    │   └── index.d.ts
    ├── main.ts
    └── tsconfig.json
    
  4. 现在,要使其与Node模块解析一起使用,您需要将两个编译器的JS输出放在同一目录中。我们还希望TypeScript为我们的TS文件发出类型声明(在npm上发布时可以使用)。我们还删除了export interface Person { name: string; } export const david: Person; TS编译器选项(不再需要),并为Node类型添加了全局类型引用。

    CLI / tsconfig.json:

    paths
  5. 差不多完成了。现在,我们需要确保PureScript模块的类型定义最终在输出目录中可供使用。生成类型定义输出时,TypeScript不会在其输出中复制{ "compilerOptions": { "target": "es5", "lib": ["es2015"], "moduleResolution": "node", "baseUrl": ".", "outDir": "./../output", "types": [ "node" ], "declaration": true }, "include": [ "*.ts" ] } 文件(因为这些文件不是由TS编译器创建的) - 我们需要对此进行补偿。我不知道这是否是实现这一目标的最好(甚至是好的?)方式,但以下方法对我有用:

    的package.json:

    .d.ts
  6. 最后但并非最不重要的是,我们需要指定JS的入口点和npm使用者的类型定义:

    的package.json:

    "scripts": {
      "build": "pulp build && tsc -P cli/tsconfig.json && cd cli && rsync -am --include='*.d.ts' -f 'hide,! */' . ./../output"
    },
    ...
    
  7. 我认为这并未涵盖创建混合语言和混合类型npm包的所有细节,但希望这可以作为一个起点。

答案 2 :(得分:0)

声明命名空间而不是模块使一切正常。例如,在types/People.d.ts

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

    export const david: Person;
}

export = People;
export as namespace People;

在编译PureScript模块之后和编译TypeScript之前,我cp types/People.d.ts output/People/index.d.ts。这使得TypeScript代码对相同的绝对导入(例如import * as People from "People";)感到满意,而TypeScript库也可以在没有其他配置的情况下看到这些类型。

但仍存在一些问题:

  • 我仍然需要在编译的TypeScript中对PureScript模块导入进行相对化处理(通过在构建后步骤中预先添加../);但是,我的图书馆的消费者不必这样做,因为它经历了npm并且神奇地使它发挥作用。
  • 我无法弄清楚如何使用句点导出名称空间,因此像Data.Maybe这样的模块由Data_Maybe等名称空间表示。
  • 我对命名空间与模块的了解不多,所以可能还有其他注意事项。