我正在创建一个TS库,它的构建涉及解析TS代码,在一个复杂的自定义进程中从它生成类型和附加变量(也就是说,不只是通过编译工具运行它)。
具体来说,这个库是几个不同API的一组OpenAPI规范。它是用TS编写的,库导出OpenAPI规范中的各种TS文件基本上都是来自TS文件的大JSON。然后,我使用手动/定制编译过程处理这些blobs,并将它们转换为一系列导出,包括端点响应类型和子类型。所有这些都不能使用tsc、webpack等自动编译,所以我的目标是用描述的“手动”编译脚本来模拟这样一个过程的输出形状。
然后,我将结果库导入到一个使用TypeScript的React应用程序中,并且还有Jest测试。值得注意的是,我的库没有默认的导出,因为它代表了几个API,因此,它是像import { SomeAPI } from "my-lib/dist/some-api";一样导入的。(/dist/,但一次只做一件事。)
我遇到了一些问题,弄清楚如何构造我构建的代码,以便它可以被导入并由应用程序和应用的Jest规范运行。
我的第一次尝试是将所有内容编译到只导出变量和类型的.ts文件。这个程序运行良好,但当我试图导入库并在“导入”应用程序中运行规范时,抛出了一个无法在模块外使用导入语句错误,因为Jest默认情况下并不是babel-process node_modules。在运行测试时,我可以尝试让这个应用程序传输我的库--根据Jest的错误消息,To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config. --但这似乎是错误的。库在编译时不应该使用直接可消费的格式吗?
试图通过在库"type": "module"文件中声明package.json来解决这个问题,然后引导我使用ts节点运行的定制编译脚本抛出一个未知文件扩展名".ts“错误。试图通过使用ts节点的ESM选项来解决这个问题会导致其他问题。
因此,我退一步,认为最好将我的文件拆分为使用.ts文件导出普通JS对象/变量的module.exports文件,以及附带的以相同名称导出匹配类型的.d.ts文件(在读取所以波斯特之后)。
这也不起作用,因为我现在收到的警告是,导入的文件不是模块,导入应用程序似乎根本认不出这些类型。
所以我有点困惑,想了解一下我应该在这里做什么。如果我要导出一个库供TS应用程序使用,我的导出应该是什么样子?
.ts和.d.ts文件并排,.d.ts文件导出(使用export/import语法)相邻变量的类型(使用modules.export语法导出)是正确的吗?或者应该有一个并行的types目录导出类型?如果是这样,消费应用程序将如何将类型映射到变量?变量文件应该改为.js吗?我需要在我的"type": "module"中设置package.json吗?等。
我试过查看其他一些TS库的代码,但它们似乎都在做复杂的定制工作,这是很难理解的。(我看了看唐塔什,那里发生了一些奇怪的魔术)。这里有基本的最佳实践吗?
下面是一些相关的文件(精简后的),以了解我目前的位置:
package.json
{
"version": "1.0.2",
"files": [
"./dist"
],
"scripts": {
"compile:openapi": "ts-node scripts/compileOpenapi.ts"
},
"engines": {
"node": "^16.14.0"
}
}(上面的编译脚本基本上循环了我的一些OpenAPI文件,并使用fs将新文件写入/dist,其中包含从OpenAPI规范中推断出来的变量和类型。)
tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"esModuleInterop": true,
"noEmit": true,
"module": "commonjs",
"moduleResolution": "node",
"strict": true,
"noImplicitAny": true,
// Allow for newer ES6 JS syntax (eg Set, array.find, etc)
"target": "es6",
"lib": [
// Allow for newer syntax and other variables/tools
"es6",
// Object.entries and Object.values
"es2017.object",
// Window, etc
"dom",
// array.includes
"es2016.array.include",
// Promises, Async/Await
"es2018"
]
},
"ts-node": {
"require": ["tsconfig-paths/register"]
}
}dist/example.ts
module.exports.defaultExample = {
title: 'whatever',
name: 'Whatever'
};dist/example.d.ts
export interface Document {
title: string;
name: string;
size?: number;
mimeType?: string;
data?: string;
}
export declare const defaultExample: Document;发布于 2022-06-15 02:19:29
我想出了一个至少对我有用的解决方案,按照@Bergi上面的建议使用tsc (tsc --outDir -d dist someFile.ts)输出声明和JS文件,然后我可以复制这些声明和JS文件的基本结构。
与其分享我的超级特定和冗长的编译代码,我认为更有用的是仅仅展示事物是如何导出的。基本结构是遵循exports导出语法的并行JS文件,以及导出类型的d.ts文件(这两种类型都是逐字匹配的JS和其他类型):
- index.js
- index.d.ts
- folder
|- something.js
|- something.d.ts所有已编译的.js文件的顶部都有以下内容:
"use strict";
exports.__esModule = true;然后将事物导出为exports.[varName]或exports.default。他们还使用const defaultExample = require("./examples/default").default;语法(或const something = require("./examples/whatever").something;)导入其他模块。如图所示
const defaultExample = require("./examples/default").default;
"use strict";
exports.__esModule = true;
exports.someArray = ["one", "two", "three"];
exports.defaultExample = defaultExample;随后,所附的.d.ts文件如下所示:
export declare interface ExampleInterface {
// ...
}
export declare const defaultExample: ExampleInterface;
export declare const someArray: string[];
// etc这种导出格式在消费应用程序中正确工作。变量(如defaultExample)是可用的,类型在悬停等方面都是自省的,它们也能顺利地与规范一起工作。奇怪的黑客解决方案(通常是某种类型的库)将完成正式的编译,但至少我发现了解TS模块是如何简单地为函数导入构建的是有帮助的。
https://stackoverflow.com/questions/72622234
复制相似问题