如何在TypeScript项目之间共享代码?

Ale*_*hin 29 typescript

假设我有两个具有以下文件结构的项目

/my-projects/

  /project-a/
    lib.ts
    app.ts
    tsconfig.json

  /project-b/
    app.ts         // import '../project-a/lib.ts'
    tsconfig.json
Run Code Online (Sandbox Code Playgroud)

我想消费也lib.ts位于.怎么做?project-aproject-b

  • 将其作为NPM模块发布 - 绝对不希望如此,对于这样一个简单的用例来说,这是一种过度杀伤力.我只想在两个项目之间共享一个文件.

  • 使用import '../project-a/lib.ts'- 不起作用,TypeScript抱怨

'lib.ts' is not under 'rootDir'. 'rootDir' is expected to contain all source files.

  • tsconfig.json一个级别,因此将涵盖project-aproject-b-不能做到这一点,打字稿配置是这些项目略有不同.它也不是很方便,不想这样做.

还有其他方法吗?

Thd*_*hdK 17

Since Typescript 3.0 this can be done with Project References.

Typescript docs: https://www.typescriptlang.org/docs/handbook/project-references.html

I believe you would have to move lib.ts into a small ts project called something like 'lib'

The lib project should have a tsconfig containing:

// lib/tsconfig.json
    {
          "compilerOptions": {
            /* Truncated compiler options to list only relevant options */
            "declaration": true, 
            "declarationMap": true,
            "rootDir": ".",   
            "composite": true,     
          },
          "references": []  // * Any project that is referenced must itself have a `references` array (which may be empty).
        }
Run Code Online (Sandbox Code Playgroud)

Then in both project-a and project-b add the reference to this lib project into your tsconfig

// project-a/ts-config.json
// project-b/ts-config.json
{
        "compilerOptions": {
            "target": "es5", 
            "module": "es2015",
            "moduleResolution": "node"
            // ...
        },
        "references": [
            { "path": "../lib" }
        ]
    }
Run Code Online (Sandbox Code Playgroud)

In the lib project. Create a file index.ts which should export all your code you want to share with other projects.

// lib/index.ts    
export * from 'lib.ts';
Run Code Online (Sandbox Code Playgroud)

Now, let's say lib/lib.ts looks like this:

// lib/lib.ts
export const log = (message: string) => console.log(message);
Run Code Online (Sandbox Code Playgroud)

You can now import the log function from lib/lib.ts in both project-a and project-b

// project-a/app.ts 
// project-b/app.ts
import { log } from '../lib';
log("This is a message");
Run Code Online (Sandbox Code Playgroud)

Before your intelissense will work, you now need to build both your project-a and project-b using:

tsc -b 
Run Code Online (Sandbox Code Playgroud)

Which will first build your project references (lib in this case) and then build the current project (project-a or project-b).

The typescript compiler will not look at the actual typescript files from lib. Instead it will only use the typescript declaration files (*.d.ts) generated when building the lib project.

That's why your lib/tsconfig.json file must contain:

"declaration": true,
Run Code Online (Sandbox Code Playgroud)

However, if you navigate to the definition of the log function in project-a/app.ts using F12 key in Visual Studio code, you'll be shown the correct typescript file. At least, if you have correctly setup your lib/tsconfig.json with:

"declarationMap": true,
Run Code Online (Sandbox Code Playgroud)

I've create a small github repo demonstrating this example of project references with typescript:

https://github.com/thdk/TS3-projects-references-example

  • 很好的答案,但从我所看到的(我肯定可能搞砸了一些事情),如果我将“project-a”作为独立项目推送到生产环境,引用的库将不会与代码捆绑在一起。看来您需要推送包含project-a *和*引用的lib的父文件夹。也许 webpack 需要“内联”引用的库?我可能有与OP不同的用例......我想做的就是拥有一些松散的打字稿文件,我希望它们成为“单一事实来源”,而不是将它们复制并粘贴到各种项目中。 (4认同)
  • 使用“prepend”选项时出现此错误:无法在项目“some/path/to/lib”前面添加,因为它没有设置“outFile”。 (3认同)

Ole*_*zky 10

这可以通过使用tsconfig.json中'CompilerOptions'的'paths'属性来实现

{
  "compilerOptions": {
    "paths": {
      "@otherProject/*": [
        "../otherProject/src/*"
      ]
    }
  },
}
Run Code Online (Sandbox Code Playgroud)

下面是文件夹结构的屏幕截图。

在此处输入图片说明

以下是引用其他ts-project的tsconfig.json的内容

{
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./tsc-out",
    "sourceMap": false,
    "declaration": false,
    "moduleResolution": "node",
    "module": "es6",
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2017",
      "dom"
    ],
    "paths": {
      "@src/*": [ "src/*" ],
      "@qc/*": [
        "../Pti.Web/ClientApp/src/app/*"
      ]
    }
  },
  "exclude": [
    "node_modules",
    "dist",
    "tsc-out"
  ]
}
Run Code Online (Sandbox Code Playgroud)

以下是引用其他项目的出口的进口声明。

import { IntegrationMessageState } from '@qc/shared/states/integration-message.state';
Run Code Online (Sandbox Code Playgroud)

  • @NicholasPetersen,我将这种方法用于两个不同文件夹中的两个不同tsconfig文件 (2认同)
  • 谢谢@奥列格!这很有帮助。我认为我的问题是这样的:初始导入 (`import { IntegrationMessageState } ...`) 似乎有效,但是当我调用导入类型的成员时,即使该类型被识别,它的成员也不可用,所以我以为它失败了。好吧,我是直接访问类型,而不是访问它的“新”实例(或调用原型属性)。所以我认为它一直在发挥作用。仅供参考:不确定确切的区别,但我最终使用了新的项目参考路线:https://www.typescriptlang.org/docs/handbook/project-references.html (2认同)

小智 6

我认为@qqilihq 的回应是正确的 - 尽管手动维护node_modules目录的内容存在明显的潜在问题。

我通过使用lerna 来管理这个很幸运(尽管那里有许多其他类似的工具,例如纱线工作区似乎有些相似,尽管我自己没有使用过它们)。

我只是事先声明,这对于您所谈论的内容来说可能有点重量级,但它确实为您的项目提供了很大的灵活性,可以在未来发展。

使用此模式,您的代码最终将类似于:

/my-projects/
    /common-code/
        lib.ts
        tsconfig.json
        package.json
    /project-a/
        app.ts (Can import common-code)
        tsconfig.json
        package.json (with a dependency on common-code)
    /project-b/
        app.ts (Can import common-code)
        tsconfig.json
        package.json (with a dependency on common-code)
Run Code Online (Sandbox Code Playgroud)

这里的一般理论是该工具在您的内部库node_modules与其依赖包的目录之间创建符号链接。

我在这样做时遇到的主要陷阱是

  • common-code必须在其文件中同时设置amaintypes属性package.json
  • common-code 必须先编译它的任何依赖项才能依赖它
  • common-code必须declaration在其设置为 truetsconfig.json

我在这方面的总体经验非常积极,因为一旦您理解了基本思想,其中就没有什么“魔法”,它只是一组碰巧共享一个目录的标准节点包。


qqi*_*ihq -5

在类似的场景中,我也想避免必须执行 NPM 发布的开销,我采用了以下结构(经过大量的尝试、错误和失败的尝试):

\n\n
/my-projects/\n\n  /node_modules/\n    /my-lib/\n      lib.ts\n      tsconfig.json\n      package.json\n\n  /project-a/\n    app.ts\n    tsconfig.json\n\n  /project-b/\n    app.ts         \n    tsconfig.json\n
Run Code Online (Sandbox Code Playgroud)\n\n

中心思想是将共享内容移动到node_modules各个项目上方的目录(这利用了 NPM 加载机制,该机制将开始在当前目录中查找依赖项,然后向上移动)。

\n\n

因此,project-a现在project-b可以通过import { Whatever } from \'my-lib\'.

\n\n

笔记:

\n\n
    \n
  1. 就我而言,my-lib实际上仅适用于共享类型(即子目录.d.ts中的文件lib)。这意味着,我不需要明确地构建my-lib,我的my-lib/package.json外观如下:

    \n\n
    {\n  "name": "my-types",\n  "version": "0.0.0",\n  "private": true,\n  "types": "lib/index"\n}\n
    Run Code Online (Sandbox Code Playgroud)
  2. \n
  3. 如果my-lib包含可运行的代码,您\xe2\x80\x99显然需要构建my-lib,以便.js生成文件,并向公开主文件的"main"属性添加一个属性。package.json.js

  4. \n
  5. 最重要的是:尽管它的名称如此,/my-projects/node_modules 但仅包含自定义代码,没有安装的依赖项(它们实际上位于各个项目project-a/node_modules和中project-b/node_modules)。这意味着,\xe2\x80\x99 有一个显式的 git 忽略设置,该设置会忽略/node_modules提交的目录。

  6. \n
\n\n

这是一个干净的解决方案吗?也许不是。它解决了我的问题吗?是的。我很高兴听到改进建议吗?绝对地!

\n

  • 这很糟糕 - `node_modules` 经常被删除。您不能指望人们甚至您自己记住您自定义的“node_modules”文件夹。你不应该打扰它 (14认同)
  • @qilihq 我要感谢您根据反对票提供这个解决方案。我意识到这东西实在是太臭了,所以我们必须尝试采用不理想的解决方案。不管怎样,加油。 (4认同)
  • @Toolkit 请随意提出更好的解决方案。由于我们**仅**将根“node_module”用于自定义模块,因此它在我们的团队中运行良好,我和我的同事都没有意外删除任何内容。它包括一个解释动机的自述文件和一个调整后的“.gitignore”文件。再次强调——欢迎更好的建议。 (2认同)