Typescript split solution into several projects - visual-studio

I used ScriptSharp before it was frozen. Since TypeScript is a developing OOP language I decided to try it. I use visual studio (if it matters). I have troubles making simple things I used to do in ScriptSharp. I didn't expect it would be that difficult.
What I want to do:
Create project A (Class Library Project) with module AssemblyA. AssemblyA module will have
some exported classes.
Create project B (Class Library Project) with module AssemblyB. AssemblyB will reference
AssemblyA types and use them as parameter types and etc.
Can you give me some guide how to make it work or sample? Thanks.
UPDATE:
What's for I can add reference to another typescript project? It would be great if output of referenced project was copied to that project.

Rather than having assemblies and modules, you have modules that can be organised into namespace-like hierarchies:
Internal Modules
Internal Module Example:
module AssemblyA {
export module ModuleA {
export class Example {
}
}
export module ModuleB {
export class Example {
}
}
}
var x = new AssemblyA.ModuleA.Example();
var y = new AssemblyA.ModuleB.Example();
You can also define these internal modules across multiple files...
modulea.ts
module AssemblyA {
export module ModuleA {
export class Example {
}
}
}
moduleb.ts
///<reference path="./modulea.ts" />
module AssemblyA {
export module ModuleB {
export class Example {
}
}
}
app.ts
///<reference path="./modulea.ts" />
///<reference path="./moduleb.ts" />
var x = new AssemblyA.ModuleA.Example();
var y = new AssemblyA.ModuleB.Example();
External Modules
And if you want to write really large applications, you can use external modules (where the file represents the module).
assemblya/modulea.ts
export class Example {
}
assemblya/moduleb.ts
export class Example {
}
app.ts
import ModuleA = require('./assemblya/modulea');
import ModuleA = require('./assemblya/modulea');
var x = new ModuleA.Example();
var y = new ModuleB.Example();

I found a workaround for my problem:
In project AssemblyA:
Specify "Combine javascript output into file" to "..\AssemblyB\AssemblyA.js".
Set up Generate Declaration files into true.
In project AssemblyB:
Add reference for intellisense in app.ts ///<reference path="../AssemblyA/AssemblyA.d.ts" />
Add reference to generated file in html: <script src="AssemblyA.js"></script>
In project B you can use any namespace aliases (for example: import AssemblyANS2 = AssemblyA.NS2;) or fully qualified name.
Put classes in different files, Use same module name and there is no need to refer to ts files.
What I didn't like is that referencing project doesn't make any sense, but I wanted steps 1-2-3-4 to be done automatically after adding reference.
Also "Redirect javascript output to directory" setting doesn't work when "Combine javascript output into one file" is specified. It's also weird that I can specify file path in second options. I expected these settings to be combined with Path.Combine.
Maybe my solution is not ideal, but it's exactly what I need. Feel free to suggest better idea.

Related

Error constructing an rxjs/Observable

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.

Defining TypeScript typing for module gives TS2665?

Trying to migrate from old tsd.json to typings.json. Previous my .d.ts had:
declare var modname: modname.modname;
declare module modname {
export interface modname {
new (): modname;
}
export interface foo {
bar: string;
}
}
declare module "mod-name" {
export = modname;
}
Then due to errors I changed the first lines to:
declare var modname: modname.modname;
declare namespace modname {
But then got:
TS2665: Module augmentation cannot introduce new names in the top level scope.
Maybe I am meant to install the Typings differently? - I notice strange scaffolding being automatically added to my definition files, which are installed with:
typings install github:user/typ/mod-name/mod-name.d.ts --save
TS2665: Module augmentation cannot introduce new names in the top level scope.
This no longer an error in the Latest Master version of typescript npm install typescript#next
Consider using this version for various reasons : https://basarat.gitbooks.io/typescript/content/docs/getting-started.html#nightly-typescript

Typescript reference comment for working

I am using Visual Studio 2013 Ultimate Update 4 and Typescript.
I have a class like MyClass.ts:
/// <reference path="interfaces/IMyClass"/>
export = MyModule;
module MyModule {
class MyClass {
constructor(myObject: IMyClass){....}
...
}
}
And another MyInterface.ts:
export = MyModule;
module MyModule {
interface IMyClass {
...
}
}
VS2013 is not acknowledging the IMyClass reference in the MyClass file (no intellisense either), but the reference is acting like VS can see it (no red underlines saying it can't find the file).
If I change the interface filename to MyInterface.d.ts, it does the same thing.
If I change the interface inside MyInterface.d.ts to this:
//export = MyModule ;
declare module MyModule {
interface IMyClass {
...
}
}
it fails as well.
If I change the interface inside MyInterface.d.ts to this:
//export = MyInterfaces ;
declare module MyInterfaces {
interface IMyClass {
...
}
}
it works.
Am I missing something? So we can only use reference comments for .d.ts files and the exported module names can be the same??
I'm finding a lot of the stuff around modules in typescript to be confusing.
Thanks in advance.
VS2013 is not acknowledging the IMyClass reference in the MyClass file (no intellisense either),
The modules MyModule between two TypeScript files that use external modules are distinct. And therefore you don't get interface IMyClass available in the second files MyModule.
Tip: You might want to review internal vs. external modules in TypeScript (hint: don't use internal ones if you are using external modules). https://www.youtube.com/watch?v=KDrWLMUY0R0&hd=1

How do I reference a Typescript enum inside a definition file

I am using Visual Studio 2013 with update 4 and Typescript 1.3 installed.
If I have a typescript file, like so:
MyEnums.ts:
export = MyEnumModule;
module MyEnumModule {
export enum AnEnum { RED, BLUE, GREEN }
}
And I have a definitions file like so:
MyDefinitions.d.ts:
declare module MyDefinitions {
interface ISomeInterface {
aProperty: string;
aMethod: () => void;
aColor: MyEnumModule.AnEnum;
}
}
I basically get an error of "Cannot find name 'MyEnumModule'"
This enum file works fine when referenced from typescript files. For instance:
SomeCode.ts:
export = MyCode;
import MyEnums = require('MyEnums');
module MyCode{
export class MyClass implements ISomeInterface {
public aColor: MyEnums.AnEnum = MyEnums.AnEnum.RED;
...and so on
My understanding is that adding either /// <reference ... or an import will not work for a .d.ts file (I tried just to be sure and it didn't appear to work either way).
Does anyone know how to reference an enum in a definition file like this?
Thanks in advance.
--Update:
Here is the error I see after trying Steve Fenton recommendations below (with a sample project I just made).
MyDefinitions.ts:
import MyEnumModule = require('../App/MyEnums');
declare module MyDefinitions {
interface ISomeInterface {
aProperty: string;
aMethod: () => void;
aColor: MyEnumModule.AnEnum;
}
}
MyEnums.ts:
export = MyEnumModule;
module MyEnumModule {
export enum AnEnum { RED, BLUE, GREEN }
}
MyClass.ts:
export = MyCode;
import MyImport = require('MyEnums');
module MyCode {
export class MyClass implements MyDefinitions.ISomeInterface {
public aColor: MyImport.AnEnum = MyImport.AnEnum.RED;
constructor() { }
aProperty: string = "";
aMethod: () => void = null;
}
}
Folder structure:
App
-MyClass.ts
-MyEnums.ts
Defintions
-MyDefintions.d.ts
Inside MyClass.ts MyDefinitions.ISomeInterface is underlined in red with hover warning "Cannot find name MyDefinitions".
I have AMD set for the project
Does anyone know how to reference an enum in a definition file like this?
There are workaround as Steve Fenton pointed out, but the system isn't designed for this. You should reference other definition files in your definition file and not reference an *implementation file * (MyEnum.ts) in a definition file.
I had a check on this and the following definition works for me, although I must admit I have never referenced "actual" code from "definition" code - but I can't think of any reason not to.
import MyEnumModule = require('MyEnumModule');
declare module MyDefinitions {
interface ISomeInterface {
aProperty: string;
aMethod: () => void;
aColor: MyEnumModule.AnEnum;
}
}
On mixing definitions and real implementations...
The type system in TypeScript is a design time and compile-time tool. When the type information is constructed at design time it makes no difference whether the type information is inferred from implementation code, taken from annotations that decorate implementations or come from an ambient declaration.
There are many use cases for mixing implementation code and ambient declarations - if you are migrating a million-line JavaScript program to TypeScript, you may not be able to migrate it from the bottom-most dependency upwards. Also, you can place ambient declarations inside of normal files - not just definition files - if you have a large program, you may not even know whether a type you place in an ambient declaration is "real" or "ambient".
The only difference between implementation code types and ambient declaration types is that the type information is right next to the implementation in real code files, and in a separate file for ambient declarations.
So... if you are having a problem using real implemented types in your ambient declaration, it is most likely caused by something that can be fixed. The example I supplied above works in a project I have in Visual Studio 2013, Update 4 - with TypeScript build configuration set to compile AMD modules. If you can share the exact details of the problem, I'm happy to help you get it working.
Having said this - if you are creating a type definition for trivial amounts of code, pasting them into a .ts file may even be faster than writing the definition - so you should make a case-by-case decision on where to spend the effort.

Is it possible require file content "as is" in TypeScript?

Typescript defines comment with xml tag <reference path=""/> to source local files to current file. But that tag could be placed only in file header before declaring any structures such us other modules.
So,
// File1.ts - correct
///<reference path="./Common.ts"/>
module Test {
export class TestClass {
}
}
// File2.ts - incorrect
module Test {
///<reference path="./Common.ts"/> // <<< Here is an compile error
export class TestClass {
}
}
Is it possible to source content of other typescript file to custom place of current file?
No. You cannot require code inplace as you have already found:
// File2.ts - incorrect
module Test {
///<reference path="./Common.ts"/> // <<< Here is an compile error
export class TestClass {
}
}
However you can effectively get the same effect by using functions.
// File1.ts - correct
///<reference path="./Common.ts"/>
module Test {
callAFunctionFoundInCommon();
export class TestClass {
}
}
In the latest version of TypeScript, it is not necessary to reference files manually like that. You can simply remove the reference and TypeScript will be able to figure everything out automatically.

Resources