通过 npm 包共享 TypeScript 类型声明 更新 那么...如何通过 npm 共享 TS 类型

问题描述

我需要在我的 React 客户端和我的 Express REST API 之间共享一些 TypeScript 类型,以保持代码干净和干燥。 由于这是一个私有项目,我不会通过@types 存储库共享这些类型,因此我遵循了 TypeScript 网站上的指南,这就是结果...

在 React 客户端中一切正常:我已将这些类型安装为开发依赖项并完美地使用它们。

在 Express API 中,我收到此错误,我认为这与我构建包的方式有关。

我做错了什么? 尽管我很无知,但我认为这与模块的加载方式有关,但我无法准确找出可能导致错误的原因。

> cross-env NODE_ENV=production node dist/index.js

internal/modules/cjs/loader.js:834
  throw err;
  ^

Error: Cannot find module '@revodigital/suiteods-types'

我如何在 API 代码中导入模块

import { AuthEntity,Roles } from '@revodigital/suiteods-types';

@Model()
export class AuthEntityModel implements AuthEntity {
  /* ... */

  role: Roles;

  /* ... */
}


包树
suiteods-types
  |_index.d.ts
  |_package.json
  |_README.md
  |_tsconfig.json

index.d.ts

export = Ods;
export as namespace Ods;

declare namespace Ods {
  /* ... */
  interface AuthEntity extends DomainObject {
    email: string;
    password: string;
    role: Roles;
    instanceId: string;
  }

  enum Roles {
    BASE,STUDENT,BUSInesS,INSTRUCTOR,ADMIN
  }
  /* ... */
}

package.json

{
  "name": "@revodigital/suiteods-types","version": "0.1.1","description": "Type declarations for suiteods project","types": "index.d.ts","scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },"author": "Revo Digital","license": "ISC","repository": {
    "type": "git","url": "git+https://github.com/revodigital/suiteods-types.git"
  },"bugs": {
    "url": "https://github.com/revodigital/suiteods-types/issues"
  },"homepage": "https://github.com/revodigital/suiteods-types#readme"
}

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs","lib": [
      "es6"
    ],"noImplicitAny": true,"noImplicitThis": true,"strictnullchecks": true,"strictFunctionTypes": true,"baseUrl": "../","typeRoots": [
      "../"
    ],"types": [],"noEmit": true,"forceConsistentCasingInFileNames": true
  },"files": [
    "index.d.ts"
  ]
}

更新

对如何摆脱命名空间感到困惑,并且仍然在模块上遇到相同的错误,现在安装为 `dependency` 而不是 `devDependency`。 文件结构和上面一样。 先谢谢您的帮助。

更新并完成 index.d.ts

export = Ods;
export as namespace Ods;

declare namespace Ods {

  type IdType = 'Carta Identità' | 'Passaporto' | 'Patente'
  
  type ODSModule = 'SCUOLA' | 'OPERATORI' | 'DRONI' | 'ODS_ROOT'

  type Role = 'BASE' | 'STUDENTE' | 'ISTRUTTORE' | 'AMMINISTRATORE' | 'UTENTE_AZIENDale'
  
  type UserScope = 'INTERNAL' | 'WHOLE'


  interface Address {
    street: string;
    city: string;
    province: string;
    CAP: string;
  }

  interface Credentials {
    email: string;
    password: string;
  }

  interface LoggedEntity {
    authEntity: AuthEntity;
    baseUser: BaseUser;
  }

  interface ModulesInstancesMap {
    SCUOLA: string;
    OPERATORI: string;
    DRONI: string;
    ODS_ROOT: string;
  }

  interface MultiTenantController {

  }

  interface Tenant {
    _id: string;
    role: Role | ODSModule;
  }

  interface TenantInfo {
    tenant: Tenant;
    relativeGodRole: Role;
  }

  interface AuthEntity extends DomainObject {
    email: string;
    password: string;
    role: Role;
    instanceId: string;
  }

  interface BaseUser extends DomainObject {
    firstName: string;
    lastName: string;
    phone: string;
    address: Address;
    scope: UserScope;
  }

  interface BelongsToModule {
    module: ODSModule;
  }

  interface Business extends DomainObject {
    businessName: string;
    pIva: string;
    tel: string;
    pec: string;
    recipientCode: string;
    address: Address;
  }

  interface DomainObject {
    _id: string;
  }

  interface HasTenant {
    tenantInfo: TenantInfo;
  }

  interface Instructor extends BaseUser {
    licenseCode: string;
  }

  interface InternalWholeSuiteUser extends BaseUser {
    modulesInstancesMap: ModulesInstancesMap;
  }

  interface InternalModuleUser extends BaseUser,BelongsToModule {
    moduleInstanceId: string;
  }

  interface School extends Business,HasTenant {
    cApr: number;
  }


  interface Student extends BaseUser {
    stateIssuedIdNumber: string;
    stateIssuedisType: IdType;
    job: string;
    businessId?: string;
  }
}

解决方法

问题

enum 类型不是纯类型。 TypeScript 编译器为此类型生成一些 JavaScript 代码。您的其余代码需要它。

在运行时,正常部署后,您的代码无法访问“开发依赖项”。只安装了依赖项。

就您的前端而言,由于 Webpack,它有一点魔力。在构建时,Webpack 会按照所有依赖项(包括开发依赖项)中的代码进行打包。因此,您的私有依赖项的编译代码在包中并且可以正常工作。

解决方案

解决方案 1:可以仅使用运行时使用的 javascript 代码发布您的包 @revodigital/suiteods-types。然后这个包就可以作为常规的依赖了。

方案二:可以在后端使用一个bundler(Webpack或者Rollup)来打包使用过的代码。私有包的打包方式和前端一样。

解决方案 3:将私有包中的类型设为“纯类型”,以便在运行时根本不需要它。用字符串的联合替换所有 enum 类型。

例如:

enum Roles {
    BASE,STUDENT,BUSINESS,INSTRUCTOR,ADMIN
  }

... 可以替换为:

type Role = "BASE" | "STUDENT" | "BUSINESS" | "INSTRUCTOR" | "ADMIN"

注意:它需要一些重构。

作为奖励的免费建议:不要保留命名空间

不建议在模块中使用 namespace。你应该摆脱它。

当前代码:

export = Ods;
export as namespace Ods;

declare namespace Ods {

  type IdType = 'Carta Identità' | 'Passaporto' | 'Patente'
  
  type ODSModule = 'SCUOLA' | 'OPERATORI' | 'DRONI' | 'ODS_ROOT'

  // ...

  interface Address {
    street: string;
    city: string;
    province: string;
    CAP: string;
  }

  // ...
}

... 应替换为:


export type IdType = 'Carta Identità' | 'Passaporto' | 'Patente'
  
export type ODSModule = 'SCUOLA' | 'OPERATORI' | 'DRONI' | 'ODS_ROOT'

// ...

export interface Address {
  street: string;
  city: string;
  province: string;
  CAP: string;
}

// ...

然后,如果您愿意,可以将模块作为命名空间导入:

import * as Ods from "@revodigital/suiteods-types";
,

除了宝贵的@Paleo 编辑之外,添加一个包含以下内容的 index.js 文件解决了问题。


index.js

module.exports = {};

更新文件结构

suiteods-types
  |_index.d.ts
  |_index.js
  |_package.json
  |_README.md
  |_tsconfig.json


那么...如何通过 npm 共享 TS 类型

如果你想共享一些 TypeScript 类型(例如在我的情况下在客户端和服务器之间),步骤(对我有用)是以下步骤。
  1. 创建一个新文件夹并使用 npm init 将其初始化为 npm 包
  2. 推断您要在实体之间共享的所有类型并将它们分组到一个 index.d.ts 文件中
  3. 仅声明“纯类型”,就我而言,converting the enums into types(并进行一些重构以适应其余代码)就足够了
  4. 添加 tsconfig.json(示例见我上面的问题)
  5. 添加一个仅包含 index.jsmodule.exports = {}
  6. 发布
  7. 将其安装为 dependency,所以 npm i --save @yourscope/yourpkg
  8. 在需要时使用它

为了发布包,我使用了 npm 和 GitHub 包。看到这些链接...