通过 npm 包共享 TypeScript 类型声明
Sharing TypeScript type declarations via npm package
我需要在我的 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
。然后这个包就可以作为常规依赖使用了。
解决方案2:可以在后端使用打包器(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 类型(例如,在我的例子中,在客户端和服务器之间),下面的步骤(对我有用)。
- 创建一个新文件夹并使用
npm init
将其初始化为 npm 包
- 推断您想要在实体之间共享的所有类型,并将它们分组到一个
index.d.ts
文件中
- 仅声明“纯类型”,就我而言 converting the enums into types(并进行一些重构以适应其余代码)就足够了
- 添加
tsconfig.json
(示例见我上面的问题)
- 添加仅包含
module.exports = {}
的 index.js
- 发布它(见下面的链接)
- 将其安装为
dependency
,因此 `npm i --save @yourscope/yourpkg
- 需要时服用
为了发布包,我使用了 npm 和 GitHub 包。看到这些链接...
我需要在我的 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
。然后这个包就可以作为常规依赖使用了。
解决方案2:可以在后端使用打包器(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 类型(例如,在我的例子中,在客户端和服务器之间),下面的步骤(对我有用)。- 创建一个新文件夹并使用
npm init
将其初始化为 npm 包
- 推断您想要在实体之间共享的所有类型,并将它们分组到一个
index.d.ts
文件中 - 仅声明“纯类型”,就我而言 converting the enums into types(并进行一些重构以适应其余代码)就足够了
- 添加
tsconfig.json
(示例见我上面的问题) - 添加仅包含
module.exports = {}
的 - 发布它(见下面的链接)
- 将其安装为
dependency
,因此 `npm i --save @yourscope/yourpkg - 需要时服用
index.js
为了发布包,我使用了 npm 和 GitHub 包。看到这些链接...