49

When trying to extend a class from a class in a node_modules the typescript compiler throws a error saying:

Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

This only happens when the base class is from a node_module.

The base class looks like:

import {Observable} from "rxjs/Observable";
export abstract class TestBase<T> {

    request(options: any):Observable<T> {
        return Observable.throw(new Error('TestBase is abstract class. Extend it and implement own request method'));
    }
}

Subclassing it in a project:

import {Observable} from "rxjs/Observable";
import {TestBase} from "@org/core";

class SocketResponse {

}

class Socket {
    request(): Observable<SocketResponse> {
        return new Observable.of(new SocketResponse());
    }
}

export class Sub extends TestBase<SocketResponse> {
    request(options:any):Observable<SocketResponse> {
        return new Socket().request();
    }
}

If the base class (TestBase) is moved from the node_module to the project it self and change the import to look like import {TestBase} from "./base"; The error disappears.

Is this due to that the compiles creates the types in different scopes for each module? I'm completely lost here.

Update:

This seems to only happen when linking the node_modules with npm link. Seems like one possible workaround for the moment is to instead of returning a type in the base class to return a interface.

More information can be found here:

https://github.com/Microsoft/TypeScript/issues/6496

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

fredrik
  • 17,537
  • 9
  • 51
  • 71
  • 2
    I think you're right. You can't extend classes that have global/ambient scope. [This thread on the Typescript GitHub repo](https://github.com/Microsoft/TypeScript/issues/3282) appears to describe some workarounds. – morphatic Jul 03 '16 at 17:32
  • 1
    I'm having this problem even without `npm link`. I tried using `npm pack` and `npm install` as a workaround, but no dice. https://github.com/robertjd/sp-ng2/pull/1 – Matt Raible Oct 27 '16 at 16:31
  • @Maxime Raineville's answer worked for me – vince Jun 23 '17 at 19:48

11 Answers11

22

I just came across a very similar issue while developing a custom module for a project. I'm not 100% sure we were having the same problem, but it looks pretty close.

How I resolved my problem

I would suggest deleting your node_modules folder and reinstalling all your dependencies.

rm -rf node_modules/
npm cache clean
npm install

That solved it for me.

What the problem was (I think)

The module I was developing had an abstract class that the main project was trying to extend. However when trying to compile the main project, the compiler would throw the same error you are getting.

After a bit of digging around, I noticed NPM would complain about an UNMET PEER DEPENDENCY when installing my module into my project. When looking inside the node_modules of the project, I noticed that my module had another node_modules folder nested inside of it with some dependencies that it shared with the main project.

I'm not certain about this, but I think NPM thought the module was expecting different version of dependencies it shared with the main project.

So the abstract class inside the module was referencing Observable from its own nested node_modules folder while the main project was referencing Observable from the top level node_modules folder.

More information

This other questions provided some insight for me when trying solve my problem:

Why does npm install say I have unmet dependencies?

Community
  • 1
  • 1
Maxime Rainville
  • 2,019
  • 23
  • 29
9

If there are multiple node_modules folders you have to add a path mapping in the host app's tsconfig. Just map @rxjs/* to the root node_modules/rxjs/*. Then everything works fine.

David Ibl
  • 901
  • 5
  • 13
8

You can add a path mapping in your tsconfig.json to map the rxjs in your dependency to the one in your node_modules folder:

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

more documentation is available at the Typescript site

JusMalcolm
  • 1,431
  • 12
  • 10
6

I got same issue.

I have following catalog structure (angular2 project)

angular
|
---- common_files
      |
      ----- package.json
      |
      ----- index.ts
      |
      ----- catalog1
            |
            ---- package.json
            |
            ---- some_file_with_service_model_comopnent.ts
            |
            ---- index.ts    - this is regular barrel file
      |
      ----- catalog2
|
---- app1
     |
     ------ package.json
|
---- apps
     |
     ------ package.json

In my common I have definied objects and services:

export class ItemBase {}

esport class SomeType extends ItemBase {}

export class ItemServiceBase {
    public getItems():Observable<ItemBase> {
         //do something
    }
}

export class SomeService extends ItemServiceBase {
    //some specific operations can go here
}

In my apps I was using items from common as follows:

import { SomeType, SomeTypeService } from "warehouse-system/products-settings";

class AttributeTypesComponent {
  private myValues : Observable<SomeType[]>;
  private service : SomeTypeService;

  constructor(){
      this.service = new SomeTypeService();
      this.myValues = <Observable<SomeType[]>> this.service.getItems();
  }
}

This were causing compilation issues: ERROR in [at-loader] src/app/some_file.ts:22:47 Type 'Observable<ItemBase[]>' cannot be converted to type 'Observable<SomeType[]>'. Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

After investigation I changed type of myValues but it still doesn't solve problem. Compilation issues changed to:

ERROR in [at-loader] src/app/some_file.ts:22:47 Type 'Observable<SomeType[]>' cannot be converted to type 'Observable<SomeType[]>'. Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'.

The final solution (workaround)

What solved my problem, was "rewriting" observable on the app side:

    this.myValues = Observable.create(subscriber => {
      this.service.getItems().subscribe(items => subscriber.next(items));
    });

This is not really nice way to solve it. In my opinion this problem is caused by a bug in npm/typescript/observables. But untill the problem is not solved on the typescript devs side, you can use this workaround as solution.

Maciej Treder
  • 11,866
  • 5
  • 51
  • 74
6

The issue will occur only when you do npm link.

Add paths to the compilerOptions of client project's tsconfig.json

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

This should work as it will specify the path from rxjs dependency.

jitenderd
  • 181
  • 3
  • 15
  • Brilliant! Everyone always leaves out the `/*` part, which seems to be required! – Drenai Oct 04 '18 at 17:17
  • Don't scan this answer too quickly -- note that **`compilerOptions` is a root-level property in your tsconfig.json file**. Note also that [your `paths` values *must be relative to your `baseUrl`, which must exist in `compilerOptions`](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping). – ruffin Oct 25 '18 at 13:41
4

I was getting a crazy error like

Type 'Observable<MessageEvent>' cannot be converted to type 'Observable<MessageEvent>'
Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'

And the reason was npm link (actually npm i ../other-project but it's quite the same thing).

My workaround was kinda cheating:

(stream as any as Observable<MessageEvent>)

LOL

André
  • 12,497
  • 6
  • 42
  • 44
  • How was `npm link` the issue, and what could you do other than this hack in code? – ruffin Oct 25 '18 at 12:26
  • Typescript thinks MessageEvent imported in my code is different than the same class in the linked module, hence the error. You could `npm pack` the lib before installing it, so you wouldn't have a link, or you could try the other answers in this post. – André Nov 04 '18 at 05:07
1

After listing rxjs modules:

npm list rxjs

+-- @angular/cli@1.6.6 | +-- @angular-devkit/core@0.0.29 | | -- rxjs@5.5.10 | +-- @angular-devkit/schematics@0.0.52 | |-- rxjs@5.5.10 | -- rxjs@5.5.10 -- rxjs@5.5.6

You can see 2 versions of the same module. My project required lower version of rxjs, and e.g. anglular/cli required higher version of rxjs.

It didn't cause any problems before but suddenly all projects with the same dependencies were throwing the same TS90010 errors e.g.:

ERROR in [at-loader] ./src/main/webapp/app/admin/user-management/user-management-detail.component.ts:23:9 TS90010: Type 'Subscription' is not assignable to type 'Subscription'. Two different types with this name exist, but they are unrelated.

Property '_parent' is protected but type 'Subscription' is not a class derived from 'Subscription'.

Finally I updated typescript version in package.json from 2.6.2 to 2.9.2 an all errors disappeared.

  • This solved my problem. One of the libraries I use has a higher dependency for _rxjs_ than Angular. I saw the exact version required, updated _rxjs_ to that version, and the problem had been solved. Thank you very much. – MÇT Jan 03 '21 at 20:50
0

For me it was my editor issue. VSCode was including an import from the declaration files (*.d.ts) while the actual class was in another place.

I replaced the declaration file import with the actual class file import.

Shnd
  • 1,846
  • 19
  • 35
0

It often happens in monorepo to confirm that all used package versions are consistent.

such as "package-a": "1.5.0" and packageA in another package is "pacakge-a": "1.4.5".

zhi.yang
  • 425
  • 5
  • 10
0

For us, the issue was in project structure, consider following:

<root>
  +-App
  |  +-package.json
  |  +-some-file-that-uses-rxjs.ts
  +-some_library
     +-package.json
     +-some-other-file-that-uses-rxjs.ts

both App and library were using rxjs references (potentially even same versions), and build classes using their references, like below:

some-file-that-uses-rxjs.ts:

import {rxjs} from "rxjs"
export class SomeFileThatUsesRxjs<T> {
  public example(t: T): Observable<T> {
    return Observable.of(t)
  }
}

some-other-file-that-uses-rxjs.ts:

import {rxjs} from "rxjs"
import {SomeFileThatUsesRxjs} from "./../some-library/some-file-that-uses-rxjs.ts"
export class SomeOtherFileThatUsesRxjs<T> {
  public someFileThatUsesRxjs = new SomeFileThatUsesRxjs();
  public example2(t: T): Observable<T> {
    return this.someFileThatUsesRxjs.example(t)
  }
}

As you might see, both of classes uses rxjs from their own, respective package.json file and define their own respective generic type T. If you unwrap paths (won't use import shorthand), it looks like you try to return something, that looks like following, pseudo-import code with extracted generic T type:

"/app/node_modules/rxjs/Observable"<"/app/T">

and in fact you are returning

"/some_library/node_modules/rxjs/Observable"<"/some_library/T">

Where both Observable and T are not compatible from compiler perspective.

In our case solution was to use only root-level package.json for common usage of rxjs

Tomas
  • 3,269
  • 3
  • 29
  • 48
-1

actually may this problem comes due to your typescript version change

try to change your typescript version

  1. You can find that in

    package.json

set to 2.4.3

  1. if not work then simply try on cli

    npm unlink typescript

    npm install typescript@2.3.4

and after installation complete packages.json

plz check it should be work in dependencies

"dependencies": {
"@angular/animations": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"core-js": "^2.4.1",
"rxjs": "5.4.3",
"typescript": "^2.3.4", <<---------------remove ^
"zone.js": "^0.8.19"

but one thing is clear it is not error this is due to environment change

if still facing problem then try reinstall node module

good luck.......

Community
  • 1
  • 1