Why does the following TypeScript code compile, but systemjs fails to load the dependencies correctly at runtime?
import { Observable } from 'rxjs';
let temp123 = new Observable<String>();
However, this works:
import { Observable } from 'rxjs/Observable';
let temp123 = new Observable<String>();
Specifically, the first code results in a .js file that contains the code:
var Observable_1 = require('rxjs');
var temp123 = new Observable_1.Observable();
but the second code generates this:
var Observable_1 = require('rxjs/Observable');
var temp123 = new Observable_1.Observable();
the line require('rxjs') fails with a 404 error because there is no file there. Why is the typescript compiler able to resolve this, but systemjs cannot load it at runtime?
Also noteworthy: This problem only happens if I do certain things with the Observable. For example, the following code works:
import { Observable } from 'rxjs';
let temp123: Observable<String> = null;
let xyz = temp123.first();
I can use the Observable, and call methods on it, without the TypeScript compiler generated a require('rxjs'). But I can't construct one, and I can't extend it either.
Versions:TypeScript 2.0.3, Systemjs 0.19.27, rxjs 5.0.0-beta.12
Why is the typescript compiler able to resolve this, but systemjs cannot load it at runtime?
That's the way it works:
when you write import { Observable } from 'rxjs';
typescript finds rxjs
folder in node_modules
with package.json
in it, which has
"typings": "Rx.d.ts"
that's type declarations file for rxjs
, and that file contains
export { Observable } from './Observable';
which makes typescript to find another type declaration file in the same folder, Observable.d.ts
, which has exported declaration for Observable
class.
That's enough for your code to compile without errors.
If your code does not actually try to use Observable
as a value, it will work, because typescript does unused reference elision - if Observable
is used for type checking only, as in your second example, there will be no require('rxjs')
call in generated javascrpt.
Now, SystemJS.
SystemJS does not have any default location to look for modules - it does not even recognise node_modules convention about package.json
file with main
property.
So, most likely, SystemJS in your example is configured like this:
SystemJS.config({
paths: {'npm:': 'node_modules/'},
map: {'rxjs': 'npm:rxjs'},
packages: {
rxjs: {
}
}
});
So, the module rxjs/Observable
imported by this line
import { Observable } from 'rxjs/Observable';
is mapped to
node_modules/rxjs/Observable.js
because rxjs
prefix matches map
entry which together with paths
maps it to node_modules/rxjs
Observable
part comes through as is
.js
extension is added because rxjs
matches with rxjs
package in systemjs config, and for any module that belongs to a package, SystemJS adds .js
extension automatically unless defaultExtension
is set to something else in that package config.
And it works, because the file node_modules/rxjs/Observable.js
exists.
And that import works with typescript too, because node_modules/rxjs/Observable.d.ts
exists too.
Finally, this does not work at runtime
import { Observable } from 'rxjs';
because it's mapped to node_modules/rxjs
url, and there is no actual file there.
You can fix it by using main
property in SystemJS package config:
packages: {
rxjs: {
main: 'Rx.js'
}
}
Now it's mapped to node_modules/rxjs/Rx.js
, and that file actually exists and exports something named Observable
, so it should work.
Checked with SystemJS 0.19.43, rxjs 5.0.3, typescript 2.1.5.