【问题标题】:Create a TypeScript Library and use it from Node.js with ES6 and TypeScript创建一个 TypeScript 库并将其从 Node.js 与 ES6 和 TypeScript 一起使用
【发布时间】:2019-06-18 19:48:46
【问题描述】:

我想创建一个 TypeScript 库作为可以在 Node.js 中使用的私有 npm 包(包括使用支持 @types 的 ES6 和 TypeScript 的 6.x)

该库的目标是从express 扩展Request 类型并提供额外的属性。

我创建了一个新的 Node.js 项目并添加了这个tsconfig.json

{
  "compilerOptions": {
    "target": "es2015",
    "module": "commonjs",
    "sourceMap": true,
    "declaration": true,
    "outDir": "./dist",
    "strict": true,
    "types": ["mocha"]
  }
}

这些是package.json的相关部分:

{
  "name": "@myscope/my-lib",
  "main": "dist",
  "scripts": {
    "build": "rm -rf ./dist && ./node_modules/.bin/tsc",
    "test": "./node_modules/.bin/mocha test"
  },
  "private": true,
  "dependencies": {
    "joi": "11.4.0"
  },
  "devDependencies":  {
    "mocha": "^5.2.0",
    "express": "^4.16.4",
    "@types/express": "^4.16.1",
    "@types/joi": "^14.3.0",
    "@types/mocha": "^5.2.5",
    "typescript": "^3.2.4"
  }
}

我的文件夹结构是这样的:

- dist
- src
  - http
  - security
- test

我在src/http 中创建了一个新的TypeScript 文件AuthenticatedRequest.ts

import {Request} from "express";
import {UserReference} from "../security/UserReference";

export interface AuthenticatedRequest extends Request {
  user: UserReference
}

src/security 包含一个UserReference.ts

import {Claim} from "./Claim";

export interface UserReference {
  claims: Claim[];
}

还有一个Claim.ts

import {IClaim} from "./IClaim";

export class Claim implements IClaim {
  type: string;
  value: string;

  constructor(type: string, value: string) {
    this.type = type;
    this.value = value;
  }
}

IClaim.ts 看起来像这样:

export interface IClaim {
  type: string,
  value: string
}

test 中,我创建了AuthenticatedRequestTests.js(纯ES6,这里没有TypeScript 来验证来自ES6 的代码完成和使用):

'use strict';

const assert = require('assert');
const Claim = require("../dist/security/Claim").Claim;

describe('req', () => {
  it('should ', done => {
    /** @type {AuthenticatedRequest} */
    const req = {};
    req.user = { claims: [new Claim('tenantId', '123')] };
    assert.equal(req.user.claims[ 0 ].type, 'tenantId');
    assert.equal(req.user.claims[ 0 ].value, '123');
    return done();
  });
});

现在我有几个问题:

  • 这是解决此问题的预期 TypeScript 方法吗?
  • 是否可以只使用require("../dist/security/Claim"); 而不是require("../dist/security/Claim").Claim;
  • 而不是使用这个jsdoc 声明/** @type {AuthenticatedRequest} */ 我想使用/** @type {myLib.http.AuthenticatedRequest} */

我还创建了一个用于集成的本地测试项目,并通过npm link 安装了我的库。

但不是使用

const Claim = require("@scope/my-lib/security/Claim").Claim;我必须用

const Claim = require("@scope/my-lib/dist/security/Claim").Claim;

我怎样才能去掉这里的dist文件夹名称?

另外,在集成测试项目中使用AuthenticatedRequestjsdoc 注释,我得到找不到类型的错误:

【问题讨论】:

  • 您可以创建src/index.ts 并从该文件中导出所有内容(例如export * from './security/Claim')。然后从您的 JS 代码中只需要 @scope/my-lib。至于const Claim = require(...).Claim,使用解构:const {Claim} = require(...)。如果您不确定导出的内容,可以console.log(require(...))

标签: node.js typescript jsdoc


【解决方案1】:

我对你问题的一部分有另一个答案。你问了

而不是使用

const Claim = require("@scope/my-lib/security/Claim").Claim;我必须用

const Claim = require("@scope/my-lib/dist/security/Claim").Claim;

这里如何去掉 dist 文件夹名称?

正如 Karol 指出的那样,您可以在 package.json 中使用 files,以便在发布库时,将 dist 目录作为包根 (plus package.json, README, etc) 发送。这是一个很好的、成熟的模式,但有一些缺点:从 github: 而不是 NPM 安装包将不起作用,npm link 也不能用于本地开发。

从最近的 Node 版本开始,您可以改用 the exports property

{
  "exports": {
    "./": "./dist/"
  }
}

现在,require("my_lib/security/Claim") 解析为 node_modules/my_lib/dist/security/Claim。如果 Typescript 自动处理这个问题会很棒,但据我所知,您还必须在您的 tsconfig.json 中指定 baseUrlpaths,至少现在是这样。

【讨论】:

  • 谢谢,这是一个有趣的补充。
【解决方案2】:

package.json

  • 应该有一个名为types(或typings)的字段告诉您的库使用者您的项目的类型定义在哪里。如果它们是由 TypeScript 生成并保存到 dist/index.d.ts,那么这就是应该使用的路径。

    "types": "./dist/index.d.ts"

  • 应该有一个名为 files 的字段,其中包含将交付给最终用户的文件/目录数组。

运行测试

  • 这是解决此问题的预期 TypeScript 方法吗?

    如果您使用 TypeScript 来开发您的库,那么没有理由不使用 TypeScript 进行测试。那里有兼容 TypeScript 的测试运行器(ts-jest 曾经很流行,现在 Jest 能够开箱即用地理解 TypeScript)。

  • 是否可以只使用require("../dist/security/Claim"); 而不是require("../dist/security/Claim").Claim;

    使用 TypeScript,可以使用几种语法。您可以使用默认导出导出 Claim 并执行以下操作:

    import Claim from "../dist/security/Claim";
    

    或:

    const Claim = require("../dist/security/Claim");
    
  • 我想使用/** @type {myLib.http.AuthenticatedRequest} */,而不是使用这个jsdoc语句/** @type {AuthenticatedRequest} */

    您将需要一个导入类型。它们看起来像这样:

    /**
     * @type {import('path/to/AuthenticatedRequest')}
     */
    const req {}
    

    路径可以是相对的或绝对的。如果您希望将本地代码库视为从 npm 安装,您可以在测试目录中创建另一个 package.json 文件并使用相对路径来解析库模块。

    "dependencies": {
      "my-library": "../path/to/the/root"
    }
    
  • 另外,在集成测试项目中使用AuthenticatedRequest的jsdoc注释,我得到了找不到类型的错误:

    这也可以通过导入类型来解决。除非类型不在全局范围内,否则您需要先导入它才能使用它。请改用import('path/to/AuthenticatedRequest')


发生了一些事情。如果您可以创建一个公共存储库来展示您的问题,我相信我们可以更轻松地帮助您解决这些问题。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2017-01-13
  • 2020-10-21
  • 2020-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多