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

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

TL;DR 我想为已编译的 PureScript 模块创建 TypeScript 类型,并将它们分发到我的 npm 包中。我非常乐意手动维护这些类型,我只是想不出我需要在 tsconfig.json(上游和下游)和 package.json.

中输入什么

我有一个布局更简单的 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

.
├── 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) 时,[=20= 生成的 require 调用] 失败,因为他们不使用绝对路径——在这种情况下,require("People") 应该是 require("./People")。在 TypeScript 中使 import 语句相对取消类型关联。

我很难实现这些目标:

如果您有从 TypeScript 获取 PureScript 模块并通过 npm 分发所有这些模块的经验,我将不胜感激一些指导!

您的 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

如果您确实需要使用绝对路径来请求您的 PureScript 包(例如 import * as People from 'People'),我认为您将需要破解节点模块解析算法(通过将您的 PureScript 输出放在 node_modules ./output 的子目录,例如)。

但如果这不是那么重要并且您可以接受相对导入(例如 import * as People from './People'),您可以执行以下操作:

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

    cli/main.ts:

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

    .
    ├── People
    │   └── index.d.ts
    ├── main.ts
    └── tsconfig.json
    
  3. 将您的 PureScript 类型更改为本地模块声明而不是全局:

    cli/People/index.d.ts:

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

    cli/tsconfig.json:

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

    package.json:

    "scripts": {
      "build": "pulp build && tsc -P cli/tsconfig.json && cd cli && rsync -am --include='*.d.ts' -f 'hide,! */' . ./../output"
    },
    ...
    
  6. 最后但同样重要的是,我们需要指定 JS 的入口点和 npm 消费者的类型定义:

    package.json:

    "main": "output/main.js",
    "typings": "output/main.d.ts",
    "files": [
      "output/"
    ]
    ...
    

我相信这并没有涵盖创建混合语言和混合类型 npm 包的所有细节,但希望这可以作为一个起点。

声明命名空间而不是模块使一切正常。例如,在 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 模块导入(通过在 post-构建步骤中添加 ../);不过,我的图书馆的消费者不必这样做,因为它经过 npm 并且神奇地使它起作用。
  • 我不知道如何导出带句点的命名空间,所以 Data.Maybe 等模块由 Data_Maybe.
  • 等命名空间表示
  • 我不太了解名称空间与模块,因此可能还有其他注意事项。