Closed josh-sachs closed 7 years ago
This is not a bug in plugin-typescript, but rather a coding error. In TypeScript, when an imported name is used solely in type position, TypeScript removes it completely.
Consider:
// TypeScript
import { Player } from 'player.interface';
// refers to the type named "Player"
export const player : Player = {
Setup() {
console.info('setup');
},
InstanceId: 'i0'
};
will transpile into
// JavaScript
export var player = {
Setup() {
console.info('setup');
},
InstanceId: 'i0'
};
The problem is that in your example code, have used Player
in value position which causes TypeScript to retain the import.
When it is used as a value, TypeScript will not strip the import because it is referenced by the emitted JavaScript
Consider:
// TypeScript
import { Player } from 'player.interface';
// refers to the value named "Player" and the type named
export const player: Player = new Player();"Player"
will transpile into
// JavaScript
import { Player } from 'player.interface';
export var player = new Player();
player.interface.ts should look like
export class Player {
constructor() { }
InstanceId: string;
Setup = (config: PlayerConfig) => {
// configure this instance in some way
return this;
};
};
in which case it is not an interface at all.
This may not be what you want but the code you have is invalid as it is somewhere between interface and class and the declarations are somewhere between static and instance.
Shit, the example code was incomplete. I've updated the function below - let me know if it would be clearer to update the original submission..
imports-player.function.ts
import { Player } from 'player.interface';
declare var Player: Player;
export function Initialize() {
const config = { };
const x = new Player().Setup(config);
console.log(x.InstanceId);
}
The interface is describing a class imported via CDN, hence the declaration that was missing. I believe this is valid Typescript but produces the error..
Odd, the following actually seems to work for me here:
import { Player } from 'player.interface';
declare var _Player: Player;
export function Initialize() {
const config = { };
const x = new _Player().Setup(config);
console.log(x.InstanceId);
}
perhaps this is a TypeScript bug?
Actually no it's not - it's a scope collision with var Player
and import { Player }
which should be a compiler error.
I was able to get the error to popup elsewhere by changing to:
import { Player as IPlayer } from 'player.interface';
declare var Player: IPlayer;
export function Initialize() {
const config = { };
const x = new Player().Setup(config);
console.log(x.InstanceId);
}
I'm still uncertain as to whether or not there is an issue here...
this describes a similar issue (albeit with rollup-plugin-typescript) to what I'm experiencing: https://github.com/rollup/rollup-plugin-typescript/issues/65
I would think however that since this plugin-typescript can transpile to js, without rollup, just fine... and that js can be rolled up just fine... that there is some issue with the transpile feeding into rollup in one build call.
Sorry if that isn't clear.
I don't think this is an issue with the plugin. When I transpile the first example using tsc:
import { Player } from 'player.interface';
declare var Player: Player;
export function Initialize() {
const config = { };
const x = new Player().Setup(config);
console.log(x.InstanceId);
}
produces
import { Player } from './player.interface';
export function Initialize() {
var config = {};
var x = new Player().Setup(config);
console.log(x.InstanceId);
}
and the import is still there which is why rollup produces the error. There are a number of ways to do what you want, but the thing to notice is that in your example declare var Player: IPlayer
declares an instance of type IPlayer, but you want it to refer to the constructor function itself (here x is an instance of type IPlayer, Player is not). I think what you are looking for is this:
export interface Player {
InstanceId: string;
Setup: (config: any) => Player;
}
export interface PlayerClass {
new (): Player;
}
and
import {PlayerClass} from './player.interface';
declare var Player: PlayerClass;
export function Initialize() {
const config = { };
const x = new Player().Setup(config);
console.log(x.InstanceId);
}
I would maybe look at the type definitions for React as they have a number of examples of this kind of thing. It may also be possible to achieve the same thing using the 'constructor' keyword in the class definition.
I need a few days to put together some more examples - please don't close.
I am having the same issue. Rollup handles exports of interfaces ok unless you are importing them in a different file and then exporting them again.
The following code fails
files/somethingwrong.ts
export interface ISomethingWrong { something?: string; }
files/index.ts
export { ISomethingWrong } from './somethingwrong';
index.ts
import { ISomethingWrong } from './files';
The following code works
files/somethingwrong.ts
export interface ISomethingWrong { something?: string; }
index.ts
import { ISomethingWrong } from './files/somethingwrong';
@thomasmeadows interfaces don't exist at runtime, they are removed by typescript during transpilation so Rollup will never see them. What exactly is the error you are seeing?
The error thrown is that the interface being imported and then exported does not exist(which it doesn't because typescript stripped out the interfaces). The export statement in files/index.ts in scenario 1 is not stripped by Typescript. It might just be a problem with the way typescript works?
Ok I see. Yes this is due to plugin-typescript compiling with 'isolatedModules' so typescript does not know that the export should be elided. There is an issue on the typescript repo here to raise an error in this situation.
In the example you give I would not expect files/index.ts to be included in the build at all because the import statement in index.ts should get elided, but probably the real code has more content and something else is including files/index.ts?
A possible solution for this would be to import * as Files from 'files/index.ts'
instead. It may also be possible to define it in a declaration file although this will lead to other issues due to systemjs extension handling.
I can confirm the workaround solves rollup for the following scenario.
// dictionary-a.ts
export type DictionaryA { [key: string]: any };
// index.ts
export { DictonaryA } from 'dictionary-a.ts' // DOES NOT WORK.
export * from 'dictionary-a.ts' // DOES WORK.
My build process runs all of my app code through plugin-typescript before systemjs performs a rollup operation.
The process fails for imported/exported interfaces. I've created an example around an interface called "Player."
player.interface.ts
imports-player.function.ts
systemjs.config.js
build
builder.buildStatic('imports-player.function.ts', 'release.js', { runtime: false, rollup: true })'
The resulting error is:
Error: 'Player' is not exported by player.interface.ts (imported by imports-player.function.ts)
I believe root cause is that plugin-typescript is correctly stripping out the interface declaration, since it is a design time construct, but is not correctly stripping out the interface import. By the time rollup gets the transpiled output, it thinks there should be an ES6 module named "Player" somewhere, but that has been stripped out (correctly) during transpile.
What should happen is that whenever plugin-typescript strips out an interface, any import of that interface should also get stripped out.