Typescript: 使用npm链接重复类型声明

创建于 2016-01-15  ·  147评论  ·  资料来源: microsoft/TypeScript

使用TypeScript 1.7.3。

假设我有以下npm软件包。
声明文件由TypeScript编译器生成,并通过此处描述的方式从其他包中引用。

套餐一

ts src:

export default class ClassA {
  private foo: string;
  bar: number;
}

ts声明:

declare class ClassA {
  private foo;
  bar: number;
}
export default ClassA;

程序包b(取决于程序包a):

ts src:

import ClassA from 'package-a';

namespace ClassAFactory {
  export function create(): ClassA {
    return new ClassA();
  }
}
export default ClassAFactory;

ts声明:

import ClassA from 'package-a';

declare namespace ClassAFactory {
  function create(): ClassA;
}
export default ClassAFactory;

程序包c(取决于程序包a和程序包b):

ts src:

import ClassA from 'package-a';
import ClassAFactory from 'package-b';

let classA: ClassA;
classA = ClassAFactory.create(); // error!!

最后一行在编译期间导致错误:

error TS2322: Type 'ClassA' is not assignable to type 'ClassA'.
Types have separate declarations of a private property 'foo'.

当我从package-a的声明中删除行private foo; ,TypeScript不会发出任何错误。
但是,此解决方法有点痛苦。

我知道将私有属性暴露给声明是设计使然的(https://github.com/Microsoft/TypeScript/issues/1532)。
我认为TypeScript在编译变量分配时应忽略私有属性。
还是对此有更好的解决方法?

@types Bug Fixed

最有用的评论

我刚刚合并了一项更改,该更改将尝试根据它们的名称和版本来检测重复的软件包,并且仅使用其中一个。 下次发布时,请使用typescript@next尝试。

所有147条评论

此处只有一个ClassA根声明,因此不会发生此错误。

好吧,很抱歉,我发现这与npm link

当我使用npm link ,软件包的安装如下,因为它只是创建符号链接。

package-c
|
-- node_modules
    |
    -- package-a
    |   |
    |   -- index.d.ts
    |   |
    |   ...
    |
    -- package-b
        |
        -- index.d.ts
        |
        -- node_modules
        |   |
        |   -- package-a
        |       |
        |       -- index.d.ts
        |       |
        |       ...
        |
        ...

如图所示,package-a看起来有两个不同的声明文件。
如果我使用npm install正常安装软件包,则不会发生这种情况,因为在这种情况下package-b中不包含package-a的声明。

我希望无论如何都会有一些解决方案,但这可能很困难并且优先级较低。

我最终没有使用npm link ,这对我来说不再重要。

足够公平,但是其他人可能:wink:

磁盘上实际上有两个文件,带有两个ClassA声明。 因此错误是正确的。 但是在比较这些类型时,我们需要考虑节点模块。 之前在https://github.com/Microsoft/TypeScript/issues/4800中已经报告了此问题,对于Enums,我们将规则更改为半名义检查。 可能对类也做同样的事情。

对于TS 1.7.5以及所有与NPM链接的相关程序包,+ 1。 我试图构建一个显示问题的测试用例,但没有。 无论我尝试了什么,TS都能很好地解决我在应用程序中出现TS2345失败的情况,据我所知,有问题的.d.ts文件的所有副本都是指向同一文件的符号链接,因此不应类型中的声明有所不同。 但是,如果Typescript发出的错误引用了声明了两种不兼容类型的文件,那就太好了,因为这可能会引起我未考虑的问题。 现在,它说有两个定义,但无助于开发人员查明问题。

解决方法是,可以在冲突的表达式上使用<any>来跳过类型检查。 显然,这可能需要您执行以前可能不需要的其他类型注释。 我希望有人可以在某个时候隔离此问题。

编辑:在我看来,NPM链接正在发挥作用

注意TS 1.8可用,已升级,并且该版本中仍然存在该问题。

感谢您在分析和记录此问题方面所做的所有工作。 我们的某些代码库中存在相同的问题。 我们移植了一些项目以正确使用package.json依赖性,但是现在在开发过程中使用npm link时看到了这一点。

有什么我可以帮助解决的问题吗?

我使用的是Lerna ,它在各个软件包之间建立符号链接,并且在那里也看到了问题。 打字稿版本2.0.3。

不幸的是,Lerna及其符号链接是一个硬性要求,因此我使用了这种讨厌的解决方法来使它能够很好地进行编译,同时仍然可以被消费者正确地进行类型检查:

export class MyClass {
  constructor(foo: Foo) {
    (this as any)._foo = foo;
  }

  get foo() {
    return (this as any)._foo as Foo;
  }
}

该类很小,所以并不是那么繁琐,而且我不希望它真正改变,这就是为什么我认为这是可以接受的解决方法。

仅供参考,由于使用npm link并收到此错误,我也因此而结束了。 有人找到解决方法吗?

@xogeny您可以详细说明npm链接如何为您造成此问题吗?

@mhegazy好吧,我开始像上面的错误一样出现这些错误(除了我从rxjs使用Observable rxjs ,即“类型'Observable'不能分配给类型'Observable')。当然,这看起来很奇怪,因为这两个模块中我都是从完全相同的rxjs版本中引用Observable ,但是在“ met”类型的地方,我遇到了一个错误。最终在@kimamula指出这个问题的地方npm link ,则会出现此错误。我和其他人一样,都解决了这个问题(在我的情况下,我创建了一个重复的接口,仅包含在一个模块中需要,而不是引用rxjs )。

这是否回答你的问题? 我问是因为我认为我的案子与这里的其他案子没有什么不同,所以我不确定这是否对您有帮助。

我们已经在TS2.0中完成了专门用于启用npm link方案的工作(请参阅https://github.com/Microsoft/TypeScript/pull/8486和#8346)。 您是否有一个示例,我可以查看npm链接对您仍然不起作用的地方?

嗯我正在运行2.0.3(已检查)。 我将尝试创建一个可复制的案例。

顺便说一下,您应该跟进这些线程,因为它们暗示从TS 2.0开始这仍然是一个问题:

https://github.com/ReactiveX/rxjs/issues/1858
https://github.com/ReactiveX/rxjs/issues/1744

我在Lerna仓库中看到的问题有些涉及,因此我在https://github.com/seansfkelley/typescript-lerna-webpack-sadness上做了一个简化版本https://github.com/TypeStrong/ts-loader/issues/324

我正在使用打字稿2.0.3,并且如上所述,我在Observable中看到此错误,例如

Type 'Observable<Location[]>' is not assignable to type 'Observable<Location[]>'. Property 
            'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

我也在Lerna monorepo软件包中实现了这一目标。 感觉像大多数但不是所有类型系统的部分都在使用realpath来唯一标识文件。 如果沿着使用符号链接路径而不是实际路径的分支行进,则会得到相同但不同的类型。

这是一个非常残酷的问题,只会影响更复杂的代码库,而且如果不采取严厉措施似乎也无法解决,所以我希望我能说服大家给予它应有的关注。 😄

如果您有一个依赖于依赖关系A的应用程序,依赖关系A依赖于依赖关系B并出售包含依赖关系B中类型的对象,则这种情况最为明显。该应用程序和依赖关系A都是npm link依赖关系B并且期望能够从中导入类型并使它们描述同一件事。

这会导致产生严重的错误消息,并且我濒临遍历并消除我库中的所有privateprotected属性,因为我已经为此花费了很多时间:

TSError: ⨯ Unable to compile TypeScript
tests/helpers/test-application.ts (71,11): Argument of type '{ initializers: Initializer[]; rootPath: string; }' is not assignable to parameter of type 'ConstructorOptions'.
  Types of property 'initializers' are incompatible.
    Type 'Initializer[]' is not assignable to type 'Initializer[]'.
      Type 'Application.Initializer' is not assignable to type 'Application.Initializer'.
        Types of property 'initialize' are incompatible.
          Type '(app: Application) => void' is not assignable to type '(app: Application) => void'.
            Types of parameters 'app' and 'app' are incompatible.
              Type 'Application' is not assignable to type 'Application'.
                Types of property 'container' are incompatible.
                  Type 'Container' is not assignable to type 'Container'.
                    Types of property 'resolver' are incompatible.
                      Type 'Resolver' is not assignable to type 'Resolver'.
                        Types of property 'ui' are incompatible.
                          Type 'UI' is not assignable to type 'UI'.
                            Property 'logLevel' is protected but type 'UI' is not a class derived from 'UI'. (2345)

非常感谢大家对此的关注; 谢谢!

@tomdale您在使用Webpack, tsc还是其他构建工具? 我的问题似乎仅在通过Webpack进行编译时才会发生(请参阅我以前的评论中的链接存储库)。

@seansfkelley看起来像https://github.com/TypeStrong/ts-node。

没错,它使用的是ts-node (用于根应用程序)。 但是,依赖项是使用tsc编译的包。

我只是遇到了这个问题,这对我们来说是一个主要问题,因为我们使用尝试将后端分成许多小的库。 在开发过程中,我们经常需要npm链接我们的仓库。 我遇到的一个具体问题促使我发现这是rxjs Observables和接口的使用:


// in repo A
export class HttpAdapter {
    request(url: string, options?: HttpRequestOptionsArgs): Observable<HttpResponse> {
        return Observable.of({});
    }
}

// in repo B
export class HttpRequestAdapter implements HttpAdapter {
    request(url: string, options?: HttpRequestOptionsArgs): Observable<HttpResponse> {
        return Observable.of({});
    }
}

如果我不npm link ,这可以工作,但是当我这样做时,我得到:

Error:(10, 14) TS2420:Class 'HttpRequestAdapter' incorrectly implements interface 'HttpAdapter'.
  Types of property 'request' are incompatible.
    Type '(url: string, options?: HttpRequestOptionsArgs) => Observable<HttpResponse>' is not assignable to type '(url: string, options?: HttpRequestOptionsArgs) => Observable<HttpResponse>'.
      Type 'Observable<HttpResponse>' is not assignable to type 'Observable<HttpResponse>'.
        Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

我唯一能提出的建议是避免private 。 由于这个问题,我不再发布任何带有private软件包,而只使用JavaScript样式的_前缀代替。 我在https://github.com/Microsoft/TypeScript/issues/7755中遇到了这个问题,这是关于为什么private插入名义类型系统而不是结构类型的类似讨论,因此我禁止了它拥有自己的项目,因为最终容易产生版本差异(例如NPM 2或使用npm link )。

@blakeembrey,当您说避免私有时,是否建议我可以更改代码中的某些内容? 我假设Observable类型定义是问题,不是吗?

@jeffwhelpley是的,很抱歉,您没有过错。 这是Observable 。 不幸的是,避免private建议非常苗条,并不完全适用于您😄也许您可以在rxjs有关使用private在其公共界面中?

编辑:我主要发表评论,因为我更早地关注了这个问题,并避免了自己的经历,但认为我也可以再次写下我的想法,而不是与https://github.com/Microsoft/TypeScript/issues/ 6496#issuecomment -255232592(其中@tomdale建议消除privateprotected ,我前一阵子也这样做了)。

@mhegazy给我的印象是,他觉得npm link没有问题。 但这似乎仍然困扰着我们和其他人。 所以我不确定这个问题在哪里? 是TS 2.0+的公认问题,还是我只是某个地方缺少解决方法?!?

我遇到了同样的问题,它似乎不是由npm link 。 如果我使用npm install file.tar.gz安装它,我仍然会得到它。 这是错误:

app/app.component.ts(46,5): error TS2322: Type 'Observable<boolean | Account>' is not assignable to type 'Observable<boolean | Account>'.
  Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

这是我的app.component.ts样子:

export class AppComponent implements OnInit {
  private user$: Observable<Account | boolean>;
  private loggedIn$: Observable<boolean>;
  private login: boolean;
  private register: boolean;

  constructor(public stormpath: Stormpath) {}

  ngOnInit() {
    this.login = true;
    this.register = false;
    this.user$ = this.stormpath.user$;
    this.loggedIn$ = this.user$.map(user => !!user);
  }

它在抱怨this.user$行。 Stormpath user$定义如下:

@Injectable()
export class Stormpath {

  user$: Observable<Account | boolean>;

@xogeny奇怪,我的理解是定义标识与文件位置相关联,这意味着它们总是会使用npm link引起问题(因为npm link ed依赖项会安装它自己的依赖项) 。 也许定义标识已更改-在TypeScript中使用文件哈希可能是一个很好的解决方法。 不幸的是,只有十多种不同的方法可以结束JavaScript中的重复模块(来自GitHub的npm installnpm install ,手动克隆,版本冲突甚至可能导致同一版本在不同位置登陆)节点的模块解析算法如何工作等)。

@blakeembrey也许。 那么是什么是这个呢?

请注意,我不是在抱怨。 我只是想弄清楚这个解决方案是否有希望。 由于@jeffwhelpley提到的所有原因

@xogeny我知道,我也正在尝试,我希望看到它能正确解决😄我阅读了链接的问题,但是它们都旨在解决符号链接的真实路径,这意味着您是否有两个(真实)文件他们仍然会发生冲突,因为它们会解析到不同的位置。 当您将npm link从一个项目转换为另一个项目时,会发生这种情况,因为这两个项目都有它们自己的依赖关系,这些依赖关系可能与npm link ed软件包中的重新导出符号不同。

编辑:我可以确认,所有问题都是由于两个文件。 npm link会触发它,因为在您刚刚链接的存储库中有一个依赖关系很简单,该依赖关系与您链接到的项目中的依赖关系相同。 一个简单的复制方法是在应用程序的两个不同级别上执行具有相同依赖项的npm install ,然后观察它们是否出错。

image

对于遵循此线程的任何人...我尝试了此处描述的解决方法,它似乎可行(到目前为止)。

我转载了这个错误。

mkdir a; cd a
npm install rxjs
echo 'import * as rx from "rxjs"; export const myObservable: rx.Observable<number>;' > index.d.ts
echo '{ "name": "a" }' > package.json

cd ..; mkdir b; cd b
npm install rxjs
npm link ../a
echo 'import * as rx from "rxjs"; import * as a from "a"; const x: rx.Observable<number> = a.myObservable;' > index.ts
tsc index.ts --target es6 --moduleResolution node

由于有rxjs两个安装,我们得到:

index.ts(1,59): error TS2322: Type 'Observable<number>' is not assignable to type 'Observable<number>'.
  Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

我有一个适用于命令行的解决方法,但Visual Studio仍然混乱不堪: https :

对于Windows + Visual Studio 2015,我的新解决方法是将xlibsrcdist文件夹自动复制到node_modules\xlib\srcnode_modules\xlib\dist文件夹中消耗项目。

如果有人需要,这是我的robocopy批处理文件脚本的重要部分:

:rerunloop
    <strong i="14">@echo</strong> watching for changes to project files..............  (Ctrl-C to cancel)

    <strong i="15">@rem</strong> xlib --> blib and slib
    <strong i="16">@robocopy</strong> .\xlib\src .\blib\node_modules\xlib\src *.*  /MIR /NJH /NJS /NDL /XD .git
    <strong i="17">@if</strong> NOT "%errorlevel%" == "0" (
        <strong i="18">@rem</strong> copy occured, so copy both

        <strong i="19">@robocopy</strong> .\xlib\dist .\blib\node_modules\xlib\dist *.*  /MIR /NJH /NJS /NDL /XD .git   
        <strong i="20">@robocopy</strong> .\xlib\src .\slib\node_modules\xlib\src *.*  /MIR /NJH /NJS /NDL /XD .git     
        <strong i="21">@robocopy</strong> .\xlib\dist .\slib\node_modules\xlib\dist *.*  /MIR /NJH /NJS /NDL /XD .git

        <strong i="22">@rem</strong>  set the src dirs readonly
        <strong i="23">@attrib</strong> +R .\blib\node_modules\xlib\src\*  /S /D
        <strong i="24">@attrib</strong> +R .\slib\node_modules\xlib\src\*  /S /D
    )
    <strong i="25">@timeout</strong> /t 1 /nobreak > NUL
<strong i="26">@goto</strong> rerunloop

很抱歉再次出现此问题,但是这对我们的项目造成了严重的拖累,因为我们无法在进行更改时执行npm link 。 如果当前的TypeScript贡献者之一可以为我提供一些有关从哪里开始寻找代码库的指导,我很乐意提供PR。

我也在为此苦苦挣扎。 我们从一个小应用程序开始采用TS,现在我们将其拆分为子模块并将其链接起来,然后……BOOM。 TS将不再编译。 这在所有TS发行标签中仍然是一个问题吗? 我目前在@rc (2.1.1)中遇到此问题。

@heruan@jeffwhelpley您可以尝试typescript@next吗,我们已经修复了一些相关问题。 并且如果您仍然遇到问题,请提供有关项目设置的更多信息。

@mhegazy我在Version 2.2.0-dev.20161129并且仍然遇到问题。 特定的问题是我有一个项目(我们称其为ProjectA),其中包含一个“接口”(使用一个类,但这是我可以将该类用作Angular 2 DI的标记),如下所示:

export class ServerAdapter {
    start(opts: ServerOptions): Observable<any> {
        return null;
    }
}

然后,在一个完全独立的项目(我们称其为ProjectB)中,该类具有一个实现第一个项目的接口的类,如下所示:

export class RestifyServerAdapter implements ServerAdapter {
    start(opts: ServerOptions): Observable<any> {
        let server = restify.createServer();
        this.addPreprocessors(server);
        this.addRequestHandler(server, opts);
        return this.startServer(server, opts);
    }

   // more stuff here that is not relevant to this issue
}

当我为ProjectB做普通的打字稿编译时,它工作正常。 但是如果我从ProjectB根目录中npm link ProjectA ,然后再次运行tsc ,我会得到:

Types of property 'start' are incompatible.
    Type '(opts: ServerOptions) => Observable<any>' is not assignable to type '(opts: ServerOptions) => Observable<any>'. Two different types with this name exist, but they are unrelated.
      Type 'Observable<any>' is not assignable to type 'Observable<any>'. Two different types with this name exist, but they are unrelated.
        Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

我无法在模拟项目中复制,我想我错过了问题的原因,因此无法复制它。 @jeffwhelpley您可以发布模拟问题的模拟项目吗? 我认为Lerna项目应该很好并且易于测试。

@heruan我将尝试进行设置。

一,仅供参考。 我想我可能已经找到解决方法。 如果我在ProjectA和ProjectB中都npm link rxjs ,则问题得到解决。 之所以这样说是有道理的,因为在那种情况下,ProjectA和ProjectB都使用相同的确切rxjs文件。 没有这些,他们在技术上将使用不同的文件(即使是相同版本):

如果您只是ProjectB的npm link ProjectA ,则:

  • ProjectB指向node_modules / rxjs
  • ProjectA作为符号链接存在于node_modules / ProjectA中,它引用的rxjs在node_modules / ProjectA / node_modules / rxjs中

但是,如果两个都使用npm link rxjs ,则这两个rxjs引用都将被符号链接到相同的全局npm位置。

无论如何,这显然仍然不理想,但至少可以使我们前进。

另外...不确定是否相关或有问题(一旦我设置测试项目,就会看到),但是我的两个库(即ProjectA和ProjectB)实际上是私有的npm仓库。

谢谢@jeffwhelpley的提示,但是由于我使用的是Lerna,所以所有模块已经相互链接,因此它们读取同一文件,但是我认为TS编译器考虑的是链接路径,而不是真正的链接路径。 我可能是错的,因为我无法在模拟项目中进行复制,而且我真的要疯了……

这里有人能以优雅的方式解决这个问题吗?

另外,请注意,这不仅仅是npm link问题,当您的共享依赖项指向其他版本时,在生产版本中也会遇到此问题。

即。 ProjectA需要[email protected] ,ProjectB使用[email protected]

当您将ProjectA安装为ProjectB中的依赖项时,您也会有重复的类型,因为将有两个Observable声明,一个在node_modules/rxjs ,一个在node_modules/project_a/node_modules/rxjs

您可以通过允许ProjectA中的rxjs版本类似于~4.9.0 ,从而使npm install不需要下载自己的版本,而是使用ProjectB版本。 但是请记住,这不仅是开发工作流程的问题。

按照@ andy-ms的建议在此处发布。 昨天再次尝试使用最新的2.0.x,但仍然可以使用。

我正在使用Angular 1的类型来获取此内容: https :

今天再次讨论这个问题,特别是符号链接的问题。 我的设置是这样的:

node_modules/
folder
  another_folder
    node_modules/ (symlinked to ../../node_modules)
    app/ (angular1 app in typescript)
    tsconfig.json
    (other build files)

如果我只有@types/angular ,则tsc运行正常。 如果我拥有整个套件( @types/angular-{animate,cookies,mocks,resource,route,sanitize} ),那么我开始收到大量类型错误:

$ npm run tsc

> [email protected] tsc D:\work\angular.io\public\docs\_examples\upgrade-phonecat-1-typescript\ts
> tsc

../../node_modules/@types/angular/index.d.ts(17,21): error TS2300: Duplicate identifier 'angular'.
../../node_modules/@types/angular/index.d.ts(18,21): error TS2300: Duplicate identifier 'ng'.
app/app.animations.ts(5,3): error TS2339: Property 'animation' does not exist on type 'IModule'.
app/app.config.ts(6,45): error TS2305: Module 'angular' has no exported member 'route'.
app/core/checkmark/checkmark.filter.spec.ts(5,22): error TS2339: Property 'mock' does not exist on type 'IAngularStatic'.
app/core/phone/phone.service.spec.ts(18,22): error TS2339: Property 'mock' does not exist on type 'IAngularStatic'.
app/core/phone/phone.service.spec.ts(23,18): error TS2339: Property 'expectGET' does not exist on type 'IHttpBackendService'.
app/core/phone/phone.service.spec.ts(30,18): error TS2339: Property 'verifyNoOutstandingExpectation' does not exist on type 'IHttpBackendService'.
app/core/phone/phone.service.spec.ts(31,18): error TS2339: Property 'verifyNoOutstandingRequest' does not exist on type 'IHttpBackendService'.
app/core/phone/phone.service.spec.ts(39,18): error TS2339: Property 'flush' does not exist on type 'IHttpBackendService'.
app/core/phone/phone.service.ts(5,33): error TS2305: Module 'angular' has no exported member 'resource'.
app/phone-detail/phone-detail.component.spec.ts(5,22): error TS2339: Property 'mock' does not exist on type 'IAngularStatic'.
app/phone-detail/phone-detail.component.spec.ts(18,46): error TS2305: Module 'angular' has no exported member 'route'.
app/phone-detail/phone-detail.component.spec.ts(20,20): error TS2339: Property 'expectGET' does not exist on type 'IHttpBackendService'.
app/phone-detail/phone-detail.component.spec.ts(32,20): error TS2339: Property 'flush' does not exist on type 'IHttpBackendService'.
app/phone-detail/phone-detail.component.ts(7,37): error TS2305: Module 'angular' has no exported member 'route'.
app/phone-list/phone-list.component.spec.ts(6,22): error TS2339: Property 'mock' does not exist on type 'IAngularStatic'.
app/phone-list/phone-list.component.spec.ts(15,20): error TS2339: Property 'expectGET' does not exist on type 'IHttpBackendService'.
app/phone-list/phone-list.component.spec.ts(26,20): error TS2339: Property 'flush' does not exist on type 'IHttpBackendService'.
node_modules/@types/angular-resource/index.d.ts(192,40): error TS2305: Module 'angular' has no exported member 'resource'.
node_modules/@types/angular/index.d.ts(17,21): error TS2300: Duplicate identifier 'angular'.
node_modules/@types/angular/index.d.ts(18,21): error TS2300: Duplicate identifier 'ng'.

我通过将基本../../node_modules/@types作为typeRoots到我的tsconfig.json

    "typeRoots": [
      "../../node_modules/@types/"
    ]

我尝试添加本地node_modules/@types ,但这没有用。

@heruan我开始做的一个hack是实现自己的npm脚本来链接/取消链接,而不是使用lerna功能。 因此,您可以执行类似lerna run link ,然后在所有package.json文件中,都有一个名为link的npm脚本,该脚本执行所有npm链接,包括(在我的情况下) npm link rxjs 。 看来工作正常,但并非绝对不理想。

@jeffwhelpley您可以在这里分享您的解决方案吗?

@yvoronen我无法共享我的所有代码,但是上面已经描述了我的解决方案。 在高层次上,我发现的关键是确保npm不仅链接您正在处理的所有本地项目,而且还确保npm链接可能导致问题的外部库(在我的情况下,rxjs是问题所在,因为Observable对象中的私有变量)。 因此,我使用lerna来管理所有本地项目,然后运行lerna run link 。 在场景下,这将在项目的每个根文件夹中调用npm run link 。 因此,您需要在package.json中定义link脚本,如下所示:

  "scripts": {
    "link": "npm link my-local-project1 && npm link my-local-project2 && npm link rxjs || true",
    "unlink": "npm unlink my-local-project1 && npm unlink my-local-project2 && npm unlink rxjs && npm i || true"
  }

希望这是有道理的,但是如果您有任何疑问,请告诉我。

我想提供一个更新。 通过与@mhegazy进行的临时交换,这就是我们的想法。

  • 我们正在考虑的一种解决方案是根据包的版本和解决方案名称区分包的概念。 我没有有关此内容的完整细节。
  • 另一个是完全扩展路径以获得符号链接的“真实”身份。 我认为这比较简单,但在处理不同版本的同一软件包时会受到更多限制,但有助于解决很多情况。

您确定您的评论属于此问题吗? 完全听起来
不同。

在2017年1月12日星期四上午3:14 Nikos [email protected]写道:

注意,我没有使用类型或npm链接AFAIK。

[image:图片]
https://cloud.githubusercontent.com/assets/216566/21887548/451d059c-d8b8-11e6-86d1-50afae4e5c2f.png

-
您收到此邮件是因为您发表了评论。
直接回复此电子邮件,在GitHub上查看
https://github.com/Microsoft/TypeScript/issues/6496#issuecomment-272137732
或使线程静音
https://github.com/notifications/unsubscribe-auth/AAUAmcMXodOvU7coymMqGzTofD4pMagpks5rRgsogaJpZM4HFcWl

@dobesv实际上是您首先看到此问题的全部原因。 TypeScript将类声明识别为两个单独的声明,因为它无法将符号链接路径与真实路径区分开。 解决方案是

  1. 展开符号链接。
  2. 检查包含的软件包是否相同。

@DanielRosenwasser对不起,我的评论是对其他人评论的回应:“在2017年1月12日,星期四,上午3:14 Nikos @* >写”……那是谁,他们说的我不再记得了,我相信他们在这个注释线程中询问一些问题,这与npm链接无关。

但是,当我在这里时,我应该提到文件名在类型中的作用方式有些有趣。 今天,我遇到了RxJS的问题,它抱怨在Observable上没有定义某某方法。 原因是HTTP库拥有自己的rxjs私有副本,该副本不同于其他所有人。 实际上,我在node_modules树中找到了四个不同的rxjs副本。

这似乎是一个有问题的方法,将继续存在问题和混乱。

如果整个文件名概念以某种方式在类型的身份中起作用,我认为这个npm链接问题也将消失。

我对它的工作方式还是有点模糊。我是TypeScript的新手。 但这给我的印象是,这种“文件名很重要”的事情与我正在使用的库(ionic2,angular2和rxjs)结合在一起引起了我很大的困惑。

问题是文件名仍然是Javascript中模块的标识,因此它不能完全消失。 有人可以扩展关于多个软件包版本的使用规范路径(符号链接的解析)存在的问题吗? 如果树中有多个软件包版本,它们将具有多个规范的绝对路径,对吗?

编辑:发布后,我意识到这一点,并且此评论很好地总结了一下:
https://github.com/Microsoft/TypeScript/issues/6496#issuecomment -257016094

tsc完全忽略excludecompilerOptions.typeRoots 。 我尝试了所有组合以忽略符号链接的路径,但它不会
它把模块看作是一条完全不同的路径,就像它在解决符号链接一样,也不了解被排除的模式。

例如,我有G:\www\cim-service-locator npm与npm link cim-service-locator 。 在我的项目路径G:\www\cim-backend ,错误显示如下:

crop

我尝试了一切可能的excludes / includes / typesRoot组合,但不能使tsc忽略它们。 使用2.2-dev.20170131

我们既使用“ npm链接”(通过@waldekmastykarz报告的SPFx问题)遇到此问题,也没有使用“ npm链接”(请参见Bug#11436)。

最终,我意识到TypeScript编译器的严格性是由于node_modules文件夹的设计古怪而实际发生的不兼容。 考虑以下示例:

A
+---B<strong i="8">@1</strong>
+---C
|   +---B<strong i="9">@2</strong>   <--- first copy of ClassB extends ClassE version 3.4
|   \[email protected]
+---D
|   \---B<strong i="10">@2</strong>   <--- second copy of ClassB extends ClassE version 3.5
\[email protected]

在此示例中,必须在子文件夹中安装B @ 2 ,以避免与AB @ 1的依赖项发生冲突。 现在,假设ClassB从ClassE扩展而来,我们有如下内容:

B / package.json

{
  "name": "B",
  "version": "2.0.0",
  "dependencies": {
    "E": "^3.0.0",
    ...
}

如果C的package.json要求[email protected] ,则ClassB的两个副本可能会以不同的基类实现结束,这意味着它们实际上不兼容的。 如果您尝试互换使用它们,则代码可能在运行时失败。

在此示例中,TS2345将防止该错误,这很好。 但是,这不是必需的:如果编译器将两个ClassB副本视为等效,则其类型系统仍将在内部保持一致并具有确定性行为。

这很重要,因为对于我们来说TS2345大多会产生错误警报。 这些错误警报迫使人们在NPM软件包之间共享类型的任何地方都写“任何内容”,这会导致其他错误。 因此,严格性造成的问题多于解决的问题。

我想提出以下解决方法:

如果所有这些条件都成立,则不要报告具有私有成员的类的TS2345:

  1. 根据通常的TypeScript鸭式输入,公共签名是兼容的
  2. 这些类在名称和版本相同的NPM软件包文件夹中定义(根据package.json)
  3. 这些类的模块具有相同的相对路径(例如“ ./lib/blah/Bdts”)

如果不满足这些条件中的任何一个,则可以报告TS2345。

@iclanton @ nickpape-msft

TypeStrong / ts-loader#468跟踪

嗨,大家好,
有人找到了解决方案?
使用RxJS将项目与另一个项目链接时,我遇到相同的问题。
谢谢 ;)

嗨,一个临时的解决方法是用Observable#from包装一个依赖返回的Observable。

尚无解决方案。 ?

这里似乎有两个问题。

ts-loader的用户对于重复的定义获得错误的错误,似乎是由于对编译器API的无效输入引起的。 TypeStrong / ts-loader#468中提供了对此修复程序。

另一个问题是,如果您在两个嵌套文件夹中的文件系统上本地安装了两次相同的软件包(相同的npm软件包+版本)(不使用npm link ),两个嵌套文件夹中包含Enums或具有私有成员的类,则将这两个包中的类型将因为不兼容而失败。
这个问题涉及更多,它将需要编译器进行其他工作才能在处理软件包之前“去重复”软件包。

如果这两种类别均不适用于您,请提出新问题,并向我们提供足够的信息以在本地重现该问题,我们很乐意进一步调查。

一种已知的解决方法是使用多项目解决方案,该解决方案将消除重复的文件夹,即确保两个node_modules子文件夹最终以符号链接的方式链接到同一目标。

Rush (我们使用的)和Lerna是示例。

@smcatala您可以解释您的解决方案。 我需要紧急修复它。 谢谢。

@ leovo2708
模块'foo':

import { Observable } from 'rxjs'
export function foo() {
  return Observable.of('foo')
}

取决于模块'foo'的其他模块中的客户端代码:

import { Observable } from 'rxjs'
import { foo } from 'foo'

Observable.of(foo()) // wrap the returned Observable
.forEach(res => console.log(res))

@ leovo2708的简单解决方案是不使用npm link

使用ts 2.1,我已经能够在单个库中使用npm链接(我们称其为xlib ,这是我的真实示例),但是您需要确保该模块与您的库( xlib )负载不会在使用项目的node_modules文件夹中重复。

我通过以下工作流程进行此操作

  1. 删除node_modules
  2. npm link xlib在我的消耗项目的node_modules创建到xlib的sym_link
  3. npm install会安装我的耗时项目的其余依赖项

自2.2以来,我并没有真正关注过此对话,也没有检查过此问题是否已更改/修改,尽管我会分享ts 2.1的解决方法

如果有用的话,我在这里将枚举案例的多个定义的最小复制放在一起: https :

@mhegazy使用npm链接时,我们确实会看到上述问题,尽管我认为这不会影响您的其余分析。

非常感谢,希望它会尽快修复。 但是,我使用了新的解决方案,使用Observable复制了所有文件。 这不是很好,而只是临时解决方案。

tsconfig.json公开了一个路径映射,将重复的依赖项添加到paths ,因此将从正确的node_modules加载而不是链接的。

{
  "compilerOptions": {
    "baseUrl": ".", // This must be specified if "paths" is.
    "paths": {
      "@angular/common": ["../node_modules/@angular/common"],
      "@angular/compiler": ["../node_modules/@angular/compiler"],
      "@angular/core": ["../node_modules/@angular/core"],
      "@angular/forms": ["../node_modules/@angular/forms"],
      "@angular/platform-browser": ["../node_modules/@angular/platform-browser"],
      "@angular/platform-browser-dynamic": ["../node_modules/@angular/platform-browser-dynamic"],
      "@angular/router": ["../node_modules/@angular/router"],
      "@angular/http": ["../node_modules/@angular/http"],
      "rxjs/Observable": ["../node_modules/rxjs/Observable"]
    }
  }
}

不知道这里是否已经提到过,但是此解决方案对我有用: https :

它与上面的@charpeni的解决方案基本相同,除了路径中没有“ ../”前缀。 (这看起来很奇怪,因为这并不意味着tsconfig.json文件将位于项目根目录的子文件夹中吗?)

这是一个好主意@charpeni。 我已经使用该路径设置来解决其他各种类似问题,但对于这一问题似乎也很理想。 实际上,我想知道在消费项目中(而不是在每个消费项目中)正确的paths设置是否会分散TypeScript编译器的注意力,而不是后续的节点解析。 如果可行,那将是O(1)骇客,而不是O(n)骇客。 本质上,这种方案将静态运行节点解析,然后将结果填充到tsconfig中。

如果有人想观看(或帮助...),我会半公开地尝试这些事情。 接下来,我可能会尝试上述想法。

https://github.com/OasisDigital/many-to-many-angular

为了更容易使用,您还可以设置以下路径:

{
    "compilerOptions": {
        "baseUrl": ".", // This must be specified if "paths" is.
        "paths": {
            "@angular/*": ["../node_modules/@angular/*"],
            "rxjs/*": ["../node_modules/rxjs/*"]
        }
    }
}

当使用多个软件包而不是单仓库架构时,npm链接是工作流程中至关重要的部分。 TS应该能够看到两个软件包完全相同,并且没有错误

@charpeni

我也遇到了这个问题。 它突然开始在星期一发生,我不确定为什么。 这确实阻碍了开发,尤其是在为我们的项目开发软件包时。 我已经尝试了多种解决方法来解决此问题。 在Windows中运行mklink /j仍然会导致此问题发生,因此它不是npm链接问题。 如果有人有解决方法或修复程序,这将是很大的帮助。

我的解决方法是npm链接根软件包和依赖项中的每个“重复”软件包,因为这样它们将再次引用相同的文件。

顺便说一句,此问题与@types无关

我们最终使用了glob模式。

@felixfbecker对于实现,可以在这里找到: https :

我有一个类似的问题,除了它与@ types / node完全相关。

libA取决于@ node / types
libB取决于libA和@ node / types
libC取决于libA,libB和@ node / types

libA构建良好。
libB npm链接到libA构建文件。
链接到libA和libB的libC npm无法进行类型检查,并显示诸如以下错误

libC/node_modules/@types/node/index.d.ts(102,6): Duplicate identifier 'BufferEncoding'.
libC/node_modules/libB/node_modules/@types/node/index.d.ts(102,6): Duplicate identifier 'BufferEncoding'.
libC/node_modules/libB/node_modules/libA/node_modules/@types/node/index.d.ts(102,6): Duplicate identifier 'BufferEncoding'.

我试过没有“运气”的“类型”和“ typeRoots”。

@charpeni看起来您的项目也依赖于@ types / node。 您对我的问题的解决方案或解决方法有任何见解?

@nicksnyder可能是因为它们是不同的版本,并且@types/node是全局声明,这意味着您不能两次定义相同的名称。 这就是为什么软件包最好不依赖于环境类型的原因,这些应该由用户提供:
https://github.com/Microsoft/types-publisher/issues/107

@nicksnyder我遇到了类似的问题,可以通过npm将所有三个项目链接到单个@ types / node安装来解决。 由于定义是在同一文件中定义的,因此不再重复

@felixfbecker我确实验证了所有项目都依赖于相同版本的节点。

FWIW libA是https://github.com/Microsoft/vscode-languageserver-node/tree/master/jsonrpc,libBhttps://github.com/Microsoft/vscode-languageserver-node/tree/master/client和libC是我自己的VS Code扩展。

@uncleramsaynpm link @types/node多少?

???
cd libA; npm link @types/node
cd libB; npm link @types/node
cd libC; npm link @types/node

我想做链接解决方案,但是我也通过删除libA和libB中的@ types / node依赖项并添加了一个refs.d.ts来解决这个问题,该文件包含对libC的node副本的本地引用。

/// <reference path='../../../path/to/libC/node_modules/@types/node/index.d.ts'/>

我发现我能够解决https://github.com/Microsoft/TypeScript/issues/9091#issuecomment -225303098中评论中的描述所遇到的问题

我们仅添加了对模块的符号链接解析的支持...

当我们使用两种不同的引用样式时,在代码库的不同位置:

/// <reference path="../node_modules/@types/library" />
/// <reference types="library" />

在node_modules被符号链接的情况下,这两个是不兼容的,因为模块分辨率(使用types= )被扩展为实数路径,而path=不是。 这导致使用tsc --listFiles ,符号链接和实路径进行编译时引用了两个不同的文件。

我们采用的解决方案是使用其中一个,但不要同时使用两者。 它还有助于在tsconfig中指定typeRoots: [] ,以避免在使用reference path=样式的情况下使编译器自动从node_modules/@types中加载类型。

我认为(至少对我们而言)更喜欢types=样式。

不过,理想情况下,最好扩展到reference path=的realpath,并检查重复的匹配路径以避免此类问题。

希望能对某人有所帮助。

@nicksnyder,您应该可以执行以下操作:

cd libA/node_modules/@types/node; npm link
cd libB; npm link @types/node
cd libC; npm link @types/node

这样,B和C指向与A完全相同的文件。

@uncleramsay请注意,它们都是相同的版本,可能以前没有

对等依赖项是在插件要说明其功能的情况下发明的。 对于这种情况,他们可以进行一些工作。 但是,如果人们开始将所有依赖项转换为对等项依赖项,从而希望避免“ npm install”重复,就会引起很多问题。

我们花了几个月的时间从内部git存储库中清除它们。 当我按照您的建议使用时,对等项依赖关系使您可以通过将其换成几个新问题来解决并行版本控制问题:

  • 整个树都被倒置了,也就是说,每个程序包现在都承担着对曾经是间接依赖程序的程序包的硬性依赖的责任,在许多情况下,他们根本不知道它的用途是什么。

  • 如果删除了对等依赖项,则这些硬依赖项可能永远不会被删除

  • 软件包作者倾向于将广泛的范围用于他们的对等版本模式,声称可以使用从未实际测试过的版本; 破碎的建筑突然成为消费者的问题

说得好。

解决方法

这是我使用的解决方法的3个选项:

选项1:使用vscode而不是Visual Studio

tsc编译器比VS2017宽容得多,可能是因为VS2017对代码(包括符号链接的node_modules )进行爬网以提供良好的智能感知,因此感到困惑。 但是我有大型复杂的多npm-module项目,所以使用VS2017 ...如果您有类似的需求,请阅读选项2和3。

选项2:发出d.ts

如果您使用outDirrootDir (在您的tsconfig.json ),则符号链接的lib模块必须发出d.ts声明,并且这些声明必须在lib模块的package.json types属性。

这是您的lib模块的tsconfig.json应该看起来像的一个示例

{
  "compileOnSave": true,
  "compilerOptions": {
    "module": "commonjs",
    "sourceMap": true,
     "declaration": true,
    "jsx": "react",
    "newLine": "LF",
    "pretty": true,
    "stripInternal": true,
    "diagnostics": true,
    "target": "es5",
    "moduleResolution": "node",
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src",
   //workaround for npm linking projects and associated dupe identifier bugs: https://github.com/Microsoft/TypeScript/issues/9566#issuecomment-287633339
    "baseUrl": "./",
    "paths": {
      "*": [
        "node_modules/@types/*",
        "*",
        "custom-dts/*"
      ]
    }
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}

而您的lib模块的package.json应该包含如下内容:

  "main": "./dist/_index.js",
  "types": "./dist/_index.d.ts",

此选项效果很好,主要问题是在Visual Studio中调试或“转到定义”或“显示所有引用”时,它将显示d.ts而不是实际的打字稿源文件,这使视觉工作室的主要优点(在大型项目中导航)

如果您想在野外看到一个实际的示例,请查看npm模块xlib v8.5.x

选项3:发出与源内联的.js (我最喜欢的选择)

您可以直接使用.ts文件来输入符号链接的lib模块! 但前提是您不使用outDir`` and rootDir in your tsconfig.json``文件。 这将使VS2017引用正常工作。 这是您需要的配置设置:

{
  "compileOnSave": true,
  "compilerOptions": {
    "module": "commonjs",
    "sourceMap": true,
     //"declaration": true,
    "jsx": "react",
    "newLine": "LF",
    "pretty": true,
    "stripInternal": true,
    "diagnostics": true,
    "target": "es5",
    "moduleResolution": "node",
    "forceConsistentCasingInFileNames": true,
    //"outDir": "./dist",
    //"rootDir": "./src",
   //workaround for npm linking projects and associated dupe identifier bugs: https://github.com/Microsoft/TypeScript/issues/9566#issuecomment-287633339
    "baseUrl": "./",
    "paths": {
      "*": [
        "node_modules/@types/*",
        "*",
        "custom-dts/*"
      ]
    }
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}

并且您的lib模块的package.json应该更改为:

  "main": "./src/_index.js",
  "types": "./src/_index.ts",

注意事项(不适用于选项3的事项)

上述的解决方法仅当每个LIB-模块发射冗余的类型信息的工作。 例如,我曾经让xlib库公开@types/async类型定义。 但是后来我有了另一个也独立引用@types/async 。 直接使用.ts文件进行键入会导致tsc async从两个lib模块导入duplicate identifier发出咆哮。 要解决此问题,您需要不从多个库模块导入相同的@types ,或者使用选项2 .d.ts解决方法

概要

希望这可以节省您花费我的时间。...总的来说,这是非常痛苦的,并且我希望打字稿团队可以修复符号链接的模块。 但是至少现在它可以工作了(在2.x之前,这基本上是不可能的)

@mhegazyhttps://github.com/Microsoft/TypeScript/issues/11916#issuecomment -257130001中发布的变通办法(变通办法2 @jasonswearingen的变体)在lerna项目中使用链接模块时为我们解决了此问题!

我只是想分享我的经验...试图从现有项目中创建/提取一个lib,以便可以在其他项目之间共享lib。

Windows 10企业版
VS 2015更新3
VS2015 2.2.2的工具
VS代码1.12.2
打字稿2.2.2
NPM:3.10.9
节点:6.9.2

我们有一个带有Angular 4.1.x前端的VS 2015 ASP.MVC项目,我们已经开始提取其中的一些组件以制作一个公共库,以便可以在其他项目中使用它们。

该库项目是使用VS Code构建的,并使用汇总将es2015,es5和umd版本以及相应的d.ts文件构建到dist文件夹中。

最终结果如下所示:

-- dist
    |
    -- mylib
        |-- <strong i="16">@myscope</strong>
            |-- mylib.es5.js
            |-- mylib.js
        |-- bundles
            |-- mylib.umd.js
        |-- src
            |-- [all the d.ts folders/files]
        |-- index.d.ts
        |-- package.json
        |-- public_api.d.ts

我已经将dist / mylib文件夹链接到我的VS 2015 / Angular 4.1.x项目的文件夹。

当我尝试在VS 2015中编译项目时,我得到的消息类型与上面针对“订阅”,“可观察的”等情况中描述的消息类型相同。

Ex: Build: Argument of type 'Subscription' is not assignable to parameter of type 'Subscription'.

如果我从库项目中临时删除了node_modules目录,我会收到新的错误消息,抱怨找不到模块:

ex: Build: Cannot find module 'rxjs/Observable'.

这使我得出以下结论:当打字稿编译器编译应用程序时,它确实会在链接库包的node_modules文件夹中查找我的库的模块定义,而不是应用程序的node_modules文件夹。

我目前尚不清楚上述建议的解决方案将提供解决此问题的方法,有人可以帮我吗?

谢谢!

@mikehutter在使用库的项目中(不在库本身中),在tsconfig中执行以下操作:

    "paths": {
      "rxjs/*": ["../node_modules/rxjs/*"]
    },

您可能需要对此稍作调整,具体取决于您的tsconfig和源代码的结果。

(与该线程中的其他线程一样,我很乐意看到一些TypeScript的改进,使此操作变得不必要。)

@mikehutter我们最近在Angular CLI中添加了一些有关使用链接库的指南,看来您的情况是您的消费者应用程序中缺少RxJ的TypeScript路径配置。 有关更多信息,请参见https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/linked-library.md

@kylecordes在点👍

谢谢@kylecordes和@filipesilva! 那就是我所需要的...

嗨!

@mhegazy ,您是否相信将其设置为2.4,否则可能会再次延迟?
解决它确实很痛苦,这迫使每个项目始终为其真正不应该关注的事物定义路径映射。

真希望这次能够进入发布!

最好,

认为尚未提及此问题,但是解决此问题的超速记方法是:

{
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
            "*": ["node_modules/*", "*"]
        }
    }
}

上面的paths条目基本上是说:对于任何模块,请先在项目根目录的node_modules文件夹中查找它,然后回退到常规规则(从此处递归遍历目录)导入已发生)。

(如果我错了,请纠正我,我仍然是TS的新手)。

paths中的条目似乎是相对于baseUrl 。 因此,如果将baseUrl设置在子文件夹中,则需要相应地更改路径定义。 例如:

{
    "compilerOptions": {
        "baseUrl": "./src",
        "paths": {
            "*": ["../node_modules/*", "*"]
        }
    }
}

@fiznool fyi是我的解决方法选项2和3(在上面的帖子中)的一部分。 我已经看到,如果您有使用相同类型的符号链接项目,仅设置paths属性是不够的。

@jasonswearingen您的帖子比我的要全面得多...感谢您解决这一问题。 如果遇到其他问题,我将确保再次参考。 😄

我们也在Google项目中看到了这一点。 如此处所述:
https://github.com/Microsoft/TypeScript/issues/9091#issuecomment -306969543

上面描述的使用baseUrl和path的变通办法似乎不起作用,tsc仍然选择重复的定义,并且没有path / baseUrl / include / exclude / etc的组合。 能够说服它。

我很好奇这是如何正常工作的,如果您有一个项目依赖于X,并且还依赖于Y,而Y则可传递地依赖于X,那么tsc如何避免两次加载类型定义?

@esprehn我强烈建议您在这里阅读我的解决方法: https :

关于该链接,我的解决方案(2和3)不仅需要设置baseUrl和路径,还需要更多,因此,如果您正在这样做,请重新阅读。 “简便”的方法是解决方案2,但我更喜欢解决方案3,因为它允许通过Visual Studio进行正确的“导航到定义”。 解决方案3的问题是,您只需要在依赖关系链中一次加载外部.d.ts定义。

希望能有所帮助。

+1我正在使用lerna,并且通过链接的依赖项包含时,包含所包含类型的子依赖项被标记为“重复”。

如果需要,我可以共享代码。

与Angular-CLI中的WebPack一样,让Angular-CLI package.json引用RXJS包,然后从任何其他项目的package.json中删除“ RXJS”包,因为该WebPack不需要使用。

这种情况可能只是符号链接不会发生,但npm-shrinkwrap.json与依赖@types/nodedependencies 。 Shrinkwrap使npm将两个相同版本的@types/node到父包中,并安装到收缩包装中。 请参阅https://github.com/KingHenne/custom-tslint-formatters/issues/5

当使用已安装@types/react的react项目并将其链接到也使用@types/react的应用程序时,也会遇到此问题

使用yarn link -ed软件包时,我遇到了同样的问题。
Rx.js引发错误: Type 'Observable<Location[]>' is not assignable to type 'Observable<Location[]>'. Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'

幸运的是,我已经通过在父包和链接包中使用相同的Rxj.s版本解决了它。
因此,就我而言,两个不同版本的Rx.js引起了问题。

使用monorepos时,这是一个巨大的阻止程序。 真希望这不再延迟,并找到2.5的方法。

编辑:😮

image

有趣。 monorepo是我的团队没有遇到此问题的一种情况,因为符号链接将所有内容都指向了一个通用的软件包文件夹。

在开发过程中链接我们自己的内部库时,我的团队经常遇到此问题。

最近,我们发现了一个名为yalc的工具,可以很好地缓解这种情况,并且实际上构成了一个很好的开发循环。 (作为tl; dr而不是链接整个程序包,它运行预发布脚本并将结果复制到链接的文件夹中)。

这个问题似乎不断被推后。 因为这确实对许多人的工作流程造成破坏,所以我们是否可能具有某种权宜之计,例如#9448中建议的“禁用评论”?

import { baz$ } from './qux';
function foo (bar$: Observable<any>) {}

foo(baz$);
> Duplicate identifier

// Makes foo's `bar$` type equal to that of `baz$`
foo(/*typescript:identicalIdentifier*/ baz$);

“禁用评论”并不比简单地在任何地方进行类型转换好得多,这在大型项目中非常痛苦。 上面我提出了以下编译器更改:

如果所有这些条件都成立,则不要报告具有私有成员的类的TS2345:

  1. 根据通常的TypeScript鸭式输入,公共签名是兼容的
  2. 这些类在名称和版本相同的NPM软件包文件夹中定义(根据package.json)
  3. 这些类的模块具有相同的相对路径(例如“ ./lib/blah/Bdts”)

如果不满足这些条件中的任何一个,则可以报告TS2345。

简化的修复方法仍然会比当前情况更好,例如,仅考虑#1,也许仅当人们通过tsconfig选择加入时。 实施起来应该很便宜。

这种方法的优点是仅禁用错误警报,但保留现有语义。 而node_modules重新映射黑客会以潜在不正确的方式更改NPM模块分辨率(例如,在我给出的假设示例

老实说,我什至不确定在Typescript中是否应该“修复”该问题,因为纯JavaScript也存在问题:如果对象的类与所提供的类不同,instanceof运算符将返回false。

因此,我宁愿期待一个更好的替代npm链接的方法,该方法可能首先挂接到require()上。

这是一个很好的见解。 你看过pnpm吗? 这是当前node_modules设计的一小步,但如果我理解正确的话,这是非常有效的一步。

不仅可以替换或不使用npm link ,而且还可以同时安装同一@types/xxx软件包的多个版本,通常从提供TypeScript声明的依赖项中提取依赖项。正如其他人所报道的那样。 随着Node社区对TypeScript的更多采用,此类软件包的数量将会增加。

@Yogu我认为这混淆了身份和平等。 即使从两个位置导入相同的程序包/版本可能表示一个类型的两个不同实例,但它们在类型上仍然相等。

想到以下(有效)代码:

type a = { c: string };
type b = { c: string };

function foo (bar: a) {}

const baz: b = { c: 'd' };
foo(baz);

我们导入的库也应如此。

从不同版本的库(以及相应的@types包)导入的类型可以相等也可以不同(如果库更新,则类型定义也应更新以反映更改)。

我刚刚合并了一项更改,该更改将尝试根据它们的名称和版本来检测重复的软件包,并且仅使用其中一个。 下次发布时,请使用typescript@next尝试。

欢呼,非常感谢您解决此问题! :-)

@pgonzal感谢

@sompylasar @types/node有点特殊,但是对于@types/react ,这是我的建议:将模块API所需的所有依赖项放入peerDependencies ,不在dependencies 。 这清楚表明,依赖关系的版本必须在父模块和子模块之间匹配(或至少兼容)。 如果父母和孩子都使用react但不交换react类型的对象,则这里不会首先出现此问题。

@harangue如果在两个不同的文件中使用相同的名称和相同的结构在一个文件中两次声明了一个类,则仍有两个不同的类被JavaScript视为不相等。 如前所述, instanceof运算符仍将区分这两个类的实例。

看到这个小提琴进行演示。

这就是我宁愿在NPM级别上解决此问题的原因(因此,如果不打算这样做,我们首先不会有多个声明)而不是在TypeScript中解决。

@Yogu :+1:因此,如果@types/node是特例,即您不能同时使用两个不同的节点版本执行程序,它将是一个版本,并且很可能是在顶级包,也许TypeScript应该将其视为特例,例如,对于每个依赖项始终使用顶级@types/node

@ andy-ms谢谢你!

下次发布时,请使用typescript @ next尝试一下。

我认为还没有发生。 我已经尝试了几个项目,但到目前为止还没有运气。 只要next可用,将很高兴在此线程中看到更新。 @ andy-ms

看起来日常构建再次停止。 最后一个是: 2.5.0-dev.20170808

cc / @DanielRosenwasser

我不确定这是否相关,但是在尝试2.5.1时,我似乎只看到了模块的一个方面,即使它已将d.ts扩展到其他地方。

如果我有一个@types可以让我们说@types/mongodb并且我有一个自定义声明来扩展该模块,即向方法中添加promisification,那么我现在有了@types/mongodb模块mongodb ,我的项目中有一个名为mongo-promisification.d.ts类的d.ts文件,其中包含mongodb模块。

因此,在上述情况下,我有一个包含import {somethingFromTypes} from "mongodb"的文件,它抱怨无法找到来自types模块的数据,即使该文件在那里并且有效,但是如果我执行了import {somethingFromMyExtendingDts} from "mongodb"精细。

编译时,我被告知模块不导出该东西,当它导出时,这种期望的行为也是如此,我只需要在我的自定义d.ts上重新导出,或者应该同时接受类型和扩展名d.ts。我有?

我正在运行最新的测试版本(Windows 10 64位),而这似乎对我来说不是固定的。

再生产

结构体

a/
  index.ts
  package.json
b/
  index.ts
  package.json

cd a
npm link
cd ../b
npm link a

a / index.ts

import { Observable } from 'rxjs/Observable';

export class Foo {
  public bar: Observable<any>;
}

b / index.ts

import { Foo } from '@rxjs-test/a';
import { Observable } from 'rxjs/Observable';

const baz = new Foo();

function qux (quux: Observable<any>) {}

// TypeError
qux(baz.bar);

b>tsc -v
Version 2.6.0-dev.20170826

b>tsc index.ts
index.ts(11,5): error TS2345: Argument of type 'Observable<any>' is not assignable to parameter of type 'Observable<any>'.
  Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

@grofit我认为您
您应该可以像这样执行mongo-promisification.d.ts

import * as mongodb from "mongodb"; // import the real mongo

// now augment it
declare module "mongodb" {
    // new stuff...
}

如果没有import ,则您处于环境中,并且正在编写新的声明,而不是扩充。

(参考手册

现在可以使用[email protected]确认这对我有用。 非常感谢@ andy-ms-这是改变游戏规则的人!

导入Observable时遇到了相同的问题通过库Typescript到angular-cli项目,功能使用代码段

函数getitems():可观察
http.get()。map(response:Response)=> {
返回response.json();
}


and when I removed the Observable<T> from function getitems() to not return anything the error disappear.
```function getitems()
http.get().map(response : Response) =>{
return <T> response.json();
}

@ Basel78我需要一个完整的示例来重现您的错误。 另外,请确保您使用的是typescript@next - angular-cli可能会让您使用的旧版本没有重复数据删除功能。

我试过[email protected] ,但仍然获得了大量的错误TS2300的:重复的标识符。
不幸的是,我不能分享整个项目。 但是这里有一些细节:
npm-link-ed包的tsconfig:

    "compilerOptions": {
        "module": "amd",
        "target": "es3",
        "sourceMap": true,
        "noEmitHelpers": true,
        "experimentalDecorators": true,
        "baseUrl": ".", // This must be specified if "paths" is.
        "paths": {
            "lib/*": [ "src/lib/*" ],
            "modules/*": [ "src/modules/*" ],
            "vendor/*": [ "src/vendor/*" ]
        },
        "typeRoots" : ["src/typings"]
    },
    "include": [
        "src/**/*.ts",
        "src/**/.*.ts", // TS ignores file names starting with dot by default
        "tests/**/*.ts",
        "tests/**/.*.ts"
    ]

主项目的tsconfig( @croc/webclient是链接包):

    "extends": "./node_modules/@croc/webclient/tsconfig",
    "include": [
        "src/**/*.ts",
        "src/**/.*.ts",
        "node_modules/@croc/webclient/src/**/*.ts",
        "node_modules/@croc/webclient/src/**/.*.ts"
    ],
    "compilerOptions": {
      "baseUrl": ".",
      "typeRoots" : ["node_modules/@croc/webclient/src/typings"],
      "paths": {
        // map runtime paths to compile-time paths
        "lib/*": [ "node_modules/@croc/webclient/src/lib/*" ],
        "modules/*": [ "node_modules/@croc/webclient/src/modules/*" ],
        "vendor/*": [ "node_modules/@croc/webclient/src/vendor/*" ]
      }
    }

@ evil-shrike您可以将您的项目简化为一个简单的示例来说明问题吗?

@mhegazy在这里
https://github.com/evil-shrike/typescript-npmlink-issue
在项目根中:

cd lib
npm link
cd ../main
npm link tstest-lib

然后在main

npm run tsc

嗨@ evil-shrike,我已将问题缩小为:
lib / tsconfig.json

{
    "compilerOptions": {
        "typeRoots" : ["src/typings"]
    }
}

main / tsconfig.json

{
    "extends": "./node_modules/tstest-lib/tsconfig",
    "include": [
        "node_modules/tstest-lib/src/**/*.ts"
    ]
}

所以我们最终都包括了这两个(从tsc --listFiles ):

/main/node_modules/tstest-lib/src/typings/jquery/index.d.ts
/lib/src/typings/jquery/index.d.ts

由于node_modules来自include而不是来自模块分辨率,所以我不认为我们将其称为realpath ,因此它最终成为两个不同的包含文件。
最好不要包含node_modules ,而是分别编译它们并以与其他任何外部软件包相同的方式导入它们。

@ andy-ms
这不容易做到。 在我的情况下,所引用的lib具有很多环境d.ts,例如,它们扩展了全局接口。 像JQueryStatic一样:

interface JQueryStatic {
    cleanData (elems);
}

或声明已加载的前缀(requirejs):

declare module "i18n!*" {
    //const m: { [key: string]: string };
    const m;
    export = m;
}

然后在lib的源代码中的某处使用它们:

const oldCleanData = $.cleanData;
$.cleanData = function (elems: JQuery) {
..
    oldCleanData(elems);
};

当这样的模块(来自lib)将从main中导入时,它将无法编译。

在lib的源代码中,应使用/// <reference types="" /> (或/// <reference path="" /> )指令来确保在导入库时出现所需的类型。 这样,它就不必在“ include”中; 导入时,引用的类型将自动添加到项目中。

@ andy-ms谢谢,我已经重构了我的库,现在正在编译npm-link-ed的项目。

不知道这些复制品是否太大,但是我通过使用angular @ nexttypescript @ next从CLI的准系统安装中得到与Observable<T> !== Observable<T>

https://github.com/intellix/angular-cli-red
https://github.com/intellix/angular-cli-blue

蓝色从红色导入并使用组件和服务

@intellix您能否得到一个仅使用tsc命令行错误的示例? 很难判断打字稿ng使用的是哪个版本。

@ andy-ms我仍然遇到这个问题。 当我尝试在我正在构建的任何vscode扩展名之间使用npm链接时,对我来说这会发生。 它们都导入vscode ,这会导致重复的标识符错误。
TS Version 2.7.0-dev.20171118

对我来说,它可以将“ rxjs / *”映射到tsconfig文件的path部分中的node_modules中的特定rxjs文件夹。
现在,使用npm链接一切都可以正常工作。

从版本更新链接到软件包的符号链接变为非符号链接时,我也看到了这种情况。

也请参见以下SO问题: https :

@dakaraphi @JoshuaKGoldberg您能提供重现这些情况的说明吗?

对于新行为,如果我们已经在$$$ package.json看到另一个具有相同“版本”值的软件包,则不应包含该软件包。 如果您有多个具有不同版本的安装,则将获得该模块的两个不同副本。 这也许可以解释为什么如果版本更新仅影响两个安装之一,则该版本更新会破坏此更新。

@ andy-ms我的示例是以下存储库:

  1. https://github.com/dakaraphi/vscode-extension-fold
  2. https://github.com/dakaraphi/vscode-extension-common

我使用本地npm install从vscode-extension-fold引用vscode-extension-common vscode-extension-fold

如果您签这些仓库,他们目前的工作,因为我必须在路径映射解决方法package.jsonvscode-extension-fold 。 但是,如果我理解正确,则不需要该解决方法。

@dakaraphi谢谢! 看来错误是由于vscode.d.ts被写为全局环境声明而不是外部模块而引起的。 我已经创建了Microsoft / vscode-extension-vscode#90。

当我尝试链接2个包,每个包都依赖于rxjs时,这对我仍然不起作用。 我正在使用[email protected][email protected] 。 两个软件包都使用完全相同的版本。 有人对此有解决方法吗?

@SamVerschueren您能否提供具体说明来重现该错误? 同时使用typescript@next

@ andy-ms我会看看我能做什么!

@ andy-ms这是一个小型复制存储库https://github.com/SamVerschueren/ts-link-6496。 我用[email protected]重现了这一点。

  1. 安装mod-amod-b两个依赖项
  2. yarn build编译mod-b yarn build
  3. yarn build编译mod-a yarn build

步骤3将失败,并显示以下错误

src/index.ts(7,15): error TS2345: Argument of type 'UnaryFunction<Observable<string>, Observable<string>>' is not assignable to parameter of type 'UnaryFunction<Observable<string>, Observable<string>>'.
  Types of parameters 'source' and 'source' are incompatible.
    Type 'Observable<string>' is not assignable to type 'Observable<string>'. Two different types with this name exist, but they are unrelated.
      Property 'buffer' is missing in type 'Observable<string>'.
src/index.ts(7,47): error TS7006: Parameter 'result' implicitly has an 'any' type.

仍然遇到[email protected]问题typescript @ next,并遇到了同样的问题。

问题的根源似乎在于,链接包仍然在自己的本地node_modules引用类型定义,而不是在可能的情况下使用链接到的node_modules类型。 结合以下事实:

  1. 全局变量不能重新定义

    • 这会导致编译器在父项目的node_modules和链接包中的node_modules都定义了全局变量时抱怨

  2. 否则,相同的类不能互相分配

    • 这会导致编译器在尝试将类型为父项目node_modules的类的变量分配给链接包返回的值时,该链接包的类型在其自己的node_modules

我已经可以使用paths config变量解决此问题。 对于模块其定义来自@types/* ,作为建议在这里,你可以简单地使用:

"paths": {
  "*": ["node_modules/@types/*", "*"]
}

如果您遇到了附带定义类型或全局变量的类型定义的程序包,则必须手动添加它们。 例如, rxjs

"paths": {
  "rxjs": ["node_modules/rxjs"],
  "rxjs/*": ["node_modules/rxjs/*"]
}

使用TS 2.8.3添加本地包时,我也遇到符号链接问题:

},
"devDependencies": {
    "@types/MyLib": "file:../MyLib/bin/npm/@types"
},

从v3开始,npm显然是使用符号链接安装这些文件,而不是复制文件。

但是,当我尝试进行编译时,编译器将链接的定义文件视为两个独立且相互冲突的文件:

node_modules\@types\MyLib\index.d.ts(3,11): error TS2300: Duplicate identifier 'Foo'.
C:/MySolution/MyLib/bin/npm/@types/index.d.ts(3,11): error TS2300: Duplicate identifier 'Foo'.

如果我改为手动复制文件,它将按预期工作。 我可以通过设置typeRoots: ["./node_modules/**/"]来解决此问题

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

Antony-Jones picture Antony-Jones  ·  3评论

jbondc picture jbondc  ·  3评论

zhuravlikjb picture zhuravlikjb  ·  3评论

blendsdk picture blendsdk  ·  3评论

bgrieder picture bgrieder  ·  3评论