通过npm包共享TypeScript类型声明 [英] Sharing TypeScript type declarations via npm package

查看:113
本文介绍了通过npm包共享TypeScript类型声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm in need of sharing some TypeScript types between my React client and my Express REST API in order to keep the code clean and DRY. Since this is a private proejct I wouldn't share these types through the @types repository, so I've followed the guide on the TypeScript website and this is the result...

React客户端中的所有功能都正常运行:我已将类型安装为开发依赖项,并完美使用了它们.

Everything is working just fine in the React client: I've installed the types as a dev dependency and used them flawlessly.

在Express API中,我收到此错误,并且我认为它与包装的结构有关.

In the Express API I get this error and I presume it has something to do with how I structured my package.

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

What am I doing wrong? As ignorant as I am I'd suppose it's related with how the modules are loaded, but I can't figure out precisely what may be causing the error.

> 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代码内部导入模块

How I import the module inside the API code

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

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

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

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


Update

Confused on how to get rid of the namespace, and still getting the same error on the module, now installed as `dependency` and not as `devDependency`. The file structure is the same as above. Thanks in advance for the help.

updated and complete 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;
  }
}

推荐答案

问题

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

The problem

An enum type is not a pure type. The TypeScript compiler generates some JavaScript code for this type. The rest of your code needs it.

在运行时,在正常部署之后,您的代码无法访问"dev依赖项".仅安装了依赖项.

At run time, after a normal deployment, your code can't access to the "dev dependencies". Only the dependencies have been installed.

对于您的前端而言,Webpack带来了一点魔力.在构建时,Webpack遵循所有依赖项(包括dev依赖项)中的代码,并将其打包.因此,您的私人依赖项的已编译代码在捆绑包中,并且可以正常工作.

In the case of your frontend, there is a little magic due to Webpack. At build time, Webpack follows the code in all the dependencies (including dev dependencies), and packs them. So the compiled code of your private dependency is in the bundle and it works.

解决方案1:仅使用运行时使用的javascript代码就可以发布您的软件包 @ revodigital/suiteods-types .然后,该程序包可以用作常规依赖项.

Solution 1 : It is possible to publish your package @revodigital/suiteods-types with just the javascript code used at runtime. And then the package can be used as a regular dependency.

解决方案2:可以在后端使用捆绑程序(Webpack或汇总)来打包使用的代码.私有软件包的打包方式与前端相同.

Solution 2 : It is possible to use a bundler (Webpack or Rollup) in the back-end to pack the used code. The private package will be packed the same way as in the front-end.

解决方案3:将专用软件包中的类型设置为纯类型"因此在运行时完全不需要它.用字符串联合替换所有 enum 类型.

Solution 3 : Make the types in the private package "pure types" so it won't be needed at all at runtime. Replace all the enum types by unions of strings.

例如:

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

...可以替换为:

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

注意:它将需要一些重构.

Notice: it will require some refactoring.

不建议在模块中使用 namespace .您应该摆脱它.

It is not recommended to use a namespace in modules. You should get rid of it.

当前代码:

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;
}

// ...

然后,如果您喜欢这种方式,则可以将模块作为名称空间导入:

Then, the module can be imported as a namespace if you prefer this way:

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

这篇关于通过npm包共享TypeScript类型声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆