-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Greatly improve code sharing and reuse options #2327
Comments
It should probably be mentioned that any solution really needs to be sensitive to the inevitability that the location of the This might be one of the major pain points of coming up with a build system that knows how to integrate code from packages. Not only are people conflicted about npm vs bower, but what if what we really need are multiple |
For your first bullet, is tsconfig not a solution to the problem you're experiencing? For the second bullet, this is something we've generally stayed out of. People have very varied build systems with a variety of linters, concatentation steps, minifiers, etc and it seems difficult for us to cover the majority of those scenarios in a satisfactory way (and convince someone to break their existing, working system to migrate to the provided TS one). Maybe the solution is to make it easier to customize pre and post build steps. We generally don't want to be in the business of creating some robust, general purpose, cross platform build system. Obviously it's important that the end to end experience be as good as possible though, so maybe we need to make changes to slot into existing workflows more easily or be better at detecting errors that might have occurred as a result of that system. For node_modules #247 is a priority and hopefully helps alleviate some of the mismatch with using TS + npm modules now. Could you elaborate on what you mean by node_modules and TS sources not being consistent beyond that? |
I know I can't speak for others, but I think a build system integrated with typescript would be a welcome change to getting mired in endless build system hacking and configuration. In an ideal world, I should be able to do this:
BUT TS does the legwork to resolve and include in the build everything that What's preventing this from being a possibility? |
I think a build system integrated with typescript would be a welcome change to getting mired in endless build system hacking and configuration. Sure, but this is really a complaint about the state of JS build systems and wishing for a great 'one build system to rule them all.' In an ideal world that would exist :) In reality peoples' needs are extremely varied which is why we have many different options (many tried to create the one true build system, now we just have a lot of different ones with different pros and cons). We don't feel like we're in any better situation to suddenly solve that problem better than all the other attempts (especially prior to ES6 having a finalized, cohesive module story). And then there's the fact that even if we actually did succeed at that, many people are not going to switch from their working build system to something new, even if the new thing is better in the abstract ('if it ain't broke...'). That said, we do want to try to slot into what exists well, and we do want to enable others to make good tools around us (like interacting with grunt/gulp/etc). What's preventing this from being a possibility? What do you mean by 'everything that script.ts references'? You mean a file without /// or imports? Or a file with ES6 style imports? Obviously right now the compiler does chase through your /// references and imports or will use a tsconfig related to that script. It sounds like you mostly want to avoid /// reference pain which is what tsconfig should help with. |
So for tsconfig, I have to manually create one for every graph of source files I want to use? Why not just have the compiler automatically infer this? If I was able to statically reference other |
So for tsconfig, I have to manually create one for every graph of source files I want to use? Not for every graph necessarily, it's up to you whether you want to explicitly list everything or let reference chasing happen depending on what references are in relevant files. Think of it like a basic project file. Why not just have the compiler automatically infer this? r.js does this when you tell it to build a file. It will recurse through all the defines and requires and concatenate all those files together. This is exactly what the compiler currently does with .ts files based on the /// references and import/require in the file. With RequireJS you explicitly list your module's dependencies, and each dependency has its own dependency list, and the loader walks through this graph. That's the same thing tsc does when you explicitly list dependencies in the form of /// or import/require. The problem is that explicitly managing /// references in this way can be painful and sometimes things get pulled in in an unexpected order and now you have to manually try to track down how the graph was traversed and what happened. Using a tsconfig to manage the project ordering can alleviate some of this pain. |
So general advice for the time being is that I should be making everything external and not really try to make internal modules? That's a distinction I'm still trying to internalize, but I was under the impression that there might be a way for me to do all this without having to set up AMD.... |
It's really up to you as far as how you want people to consume your code. Given that ES6 only has external modules it's certainly possible that's the right choice for you going forward though. The TypeScript compiler is only internal modules but you can find small npm packages that provide access to it through external modules. Likewise you'll see some .d.ts files written in such a way as to work with both internal and external (I believe knockout is one example like this). |
I'm still at a loss to really understand what the difference is between internal and external. Some of it flies over my head. |
I assume you've read https://github.com/Microsoft/TypeScript/wiki/Modules and http://www.typescriptlang.org/Handbook#modules ? Don't feel bad, it is unfortunately confusing and a situation we definitely can improve. I'm reticent to try to give an off the cuff primer since we've already spent a bunch of time trying to wordsmith the content in those two places since this confusion definitely occurs with some frequency. The simplest distinction is that external modules are intended to be used with a module loader (either AMD or CommonJS) for all the reasons module loaders are good (plenty written on that topic for JS already). Think of internal modules simply as namespaces. Internal modules leave you with some JS (either many files or a single concatenated output files) that someone consumes by simply putting your file in scope (ex a <script> tag or concatenating with their own JS) without a separate module loader. |
So if I'm making the actual entrypoint script for a page, is it possible to author an internal module while also gaining the ability to consume javascript modules? Or am I required to be producing a module so that I can consume other modules? |
Hi, here's the pattern I've been using for more than a year and a half now (for very large TS apps with more than a hundred source files). Though it may appear to stand in contrast to much of the design recommendations and advice you'll get from the TypeScript team, it has served me very well:
module App {
export class SomeClass {
...
}
}
module App {
var runningInNodeJS = () => (typeof require == "function") && (typeof module == "object")
if (runningInNodeJS())
module.exports = App;
}
import fs = require("fs");
module App {
if (runningInNodeJS()) {
var NodeFileSystem: typeof fs = require("fs");
}
} EDIT: The compiler will not emit anything for the EDIT: Removed solved problem: solution is in #2346 (comment) I published a library using exactly this pattern: Here's the compiled release file, I linked to the specific line where the internal module CommonJS export pattern is used: BTW I'm aware that the next version of TS will have ES6 modules but I haven't really looked deeply into them, though from what I've seen so far I don't feel I'd eventually make use of them. |
As for build automation, I agree that there's no "one" optimal build system, but here's my own current workflow:
function generateTypeScriptReferencesFile(filters, outFilename)
{
var fileNames = [];
for (var i=0; i < filters.length; i++)
fileNames = fileNames.concat(grunt.file.expand(filters[i]))
function getPriorityByFileExtension(filePath)
{
if (/.+\.d.ts$/.test(filePath))
return 0;
else if (/.+\.ext.ts$/.test(filePath))
return 1;
else if (/.+\.spec.ts$/.test(filePath))
return 3;
else
return 2;
}
fileNames.sort(function (fileName1, fileName2) { return getPriorityByFileExtension(fileName1) - getPriorityByFileExtension(fileName2) });
fileContent = fileNames.reduce(function(result, filePath) { return result + "/// <reference path=\"" + filePath + "\"/>\n"; }, "");
grunt.file.write(outFilename, fileContent);
grunt.log.writeln(fileNames.join("\n"));
}
...
generateTypeScriptReferencesFile(["**/*.ts"], "references.ts");
An example Gruntfile.js (for LZ-UTF8) can be found here: (Note this is a truly cross-platform solution and can also be run in Linux - I have it successfully building on a remote Linux container with Travis-CI) Hope that was useful :) |
I guess I'll spend the next 5 days inventing my own build setup... 👎 |
@danquirk - It seems like every explanation I read only gives a partial understanding, but I never get a full picture that I can put into practice. For example: I'm trying to publish just some typescript interfaces as a package for two projects to communicate along. Literally no JavaScript will be produced (for now). The top of my files read:
When I leave To compound the confusion, I'm normally used to structuring my files so that the namespaces are represented in a directory structure. There's a contradiction in my mind with explicitly naming modules and the way AMD works where the namespacing is usually just expressed through the filesystem. At which point, should I even bother namespacing any of my typescript code? |
So now I've also gone and split my interfaces into multiple files within a directory structure. |
@atrauzzi can you explain what you want to model? some concrete details would go a long way. |
I'm kind of past it at this point. I'm looking at JSPM right now, although I do hope that once TypeScript sorts out all these usability and code sharing issues, you guys will have an official plugin for JSPM. I really love what you're trying to bring to the ecosystem, it's just not easy enough to use. |
Just going to add a reference here: #2233 As I mentioned, it would be great if there was a Microsoft-maintained plugin that built to ES6 and integrated nicely with the rest of the JSPM ecosystem. @mhegazy : One of the issues is that the system is complex enough that I definitely throw up my hands trying to come up with an explanation. I'm not at all against a chat or email exchange with anyone who might want to tease out the sticking points. More than happy to be the guinea pig! :) |
@atrauzzi there is a lot of abstract conversations going on in this thread, and i am finding it hard to get to the bottom of the issue(s) you are running into, or the ones causing you to feel that the system is too complex. If i understand correctly you are basically asking for the compiler to support node resolution process as described in issue #247. is this correct? if not it would be great if you can share some concrete information on what is it that you are trying to do and how the compiler is not helping, this way we can come up with specific fixes to simplify the workflow. On a related note, @vladima is working on a proposal that handles some of the common module issues as specified in #2338, take a look and let us know if that would simplify your use case. |
Regarding #247, I think that should be amended to support JSPM, which I think is the best JS workflow I've seen to date. To give you some some idea of the impact, I went from frustrated with TypeScript, to coding and running working ES6 in the span of a day. Whatever that project is doing, it was basically everything necessary to solve my issues. Okay, with that established, I'll give this another shot and I'll try to isolate it in terms of what I go through in trying to use typescript. This is gonna be pretty dry! ;) The initial authoring of my first typescript file brings up a lot of questions where I have to stop what I'm doing and research modules. When I see modules, I think namespaces and so my first attempt is to use them as such. This means that I'm going to create a hierarchy of directories containing files. What confuses me there though is that it looks like modules have no corollary in ES6. So I don't know if they're denoting a TypeScript design-time boundary, or a compiled-JavaScript boundary. Moreover, their purpose is ultimately unclear, given that there are explicit export keywords, similar to ES6. The behaviour of exporting when nested in a module declaration is odd. The module/namespacing confusion gets more difficult when I'm trying to understand the impact it has on my ability to reuse code within my own project. When am I required to explicitly import, and when is an exported item from another file is available in my current scope? Because I have to consider both static and design time, I end up very confused. Even more befuddling is that there are two import mechanics. One is a straight up keyword, and the other is a frightening comment-hack. I just don't know when I'm safe to use something anymore because I don't know what keywords or constructs are going to require additional wiring from me at build time. Because TypeScript is your own ball game, why not just make the import keyword support importing But now that exposes a huge huge huge point of confusion (I'm glad I typed it): What happens when you import a Continuing along this packaging+runtime thread, is the fact that if I want to publish interfaces to a package, I'm completely without a clue as to how to go about setting that up. I made a hamfisted attempt on Thursday to do this and just walked away from it. While I was able to define a hierarchy of namespaces full of interfaces, I had no way of producing a single So yes, I can author typescript files and pretend like I know what I'm doing. But when it comes time to compile, I feel like the compiler isn't clear on what it needs for one file to be able to resolve all its dependencies. I feel like it's unclear as to how the code I author is going to be called into at runtime, or even where it's going to live, both in my project and in packages I need to make. I'm really hoping that you can glean something from this description. While I know it is mostly a summary of what I went through in this discussion, I just have no idea how to describe it any better. What might be nice is if you asked me specific questions based on all this. What I can suggest is based on what I've seen from JSPM and how quickly I got up and running with it:
It's all fairly abstract because this is as far as I've gotten. If at the end of the day this isn't enough, then the only thing I can say is: I have absolutely no idea how to set up my project to use TypeScript, there's just too much going on and there are too many sideffects and interactions that cross design, build and runtime boundaries to devise a mental model for it. |
Thanks @atrauzzi for the details. i will need to read this again, but i got a few of concrete points, let me summarize some action items:
Again i will need to read this again in the morning to get more details. Thanks for the feedback, really appreciate it. |
A quick addendum to namespacing corrections: Be sure to also go through it with a fine tooth comb and document their impact on design, build and runtime. There needs to be some emphasis on what they are to typescript and what they are to compiled code. I'm sure you'll infer a few more details from my story above over time, but again - please ask me any time if you want to pick at a detail. I am still very interested in TypeScript and would love to see it as a language option just like traceur and babel in JSPM. That ecosystem really seems to "get it" and is coming together nicely. |
I think it's worth noting that TS as it stands does an excellent job of supporting the various ways in which JS is currently used. It's best understood as a tool for adding static type checking to the existing JS ecosystem(s), so it has to be flexible. Some of @atrauzzi's questions are similar to questions I had when I started with TS and it was frustrating that they did not seem to be clearly answered anywhere. Now I get it, it seems like an elegant mapping to how JS is used in the real world.
Neither. At the top of a module, when you say:
you are saying two things:
(for CommonJS, or something conceptually equivalent if you choose AMD). You are also telling the compiler, only at compile time, to find type information for "x". It tries the following:
|
So when distributing JS packages, I should also include a .ts file alongside every file so that TypeScript can "prefer" it when searching for dependencies? Is this documented behaviour? |
EDIT - this comment is no longer remotely true as of TS 2.0! Going across package boundaries is totally different, unfortunately. TS doesn't know anything about npm (for example). When the consumer of an npm module says:
TS doesn't know that the implementation lives in The workaround is that the consumer must obtain from somewhere (e.g. DefinitelyTyped) a d.ts file that contains:
The consumer has to separately arrange to reference this file in their compilation step, which undermines the otherwise automatic nature and experience of npm packages. But a package manage like jspm that supports transpiler plugins could make such assumptions. I've posted some vague wishes about this under issue #2233. Such plugins take advantage of the host-ability of the TS compiler service, so they can add the necessary smarts to find type information automatically. This is the right way to do it, so TS itself is agnostic and doesn't become tied to specific ways of doing packages (new ways appear every week...) |
Fantastic, thank you so much for all this information :) Hopefully it's enough to smooth the workflow so that I can switch over to TS at some point in the future! |
Creating and sharing pure typescript code has quite a few barriers that seem like the kind of complexity that TypeScript should be helping to alleviate.
I'm going through a situation right now where I want to share a pure typescript library and - unless I'm mistaken - it seems like I'm going to have to go through the pain of adding AMD to my project. Even despite the fact that it's all internal modules.
With the overall goal of making life easier for developers, I think it would be nice if:
///
syntax used to load.d.ts
files..d.ts
definitions for example).While AMD/CommonJS are module-loading systems, they are very confusing and cluttered build systems, especially once you bring TypeScript into the mix.
It's conceivable that with these changes, you'd also reap the ability to create build profiles implicitly by defining entrypoint scripts and having typescript crawl the dependency graph. Similar to how r.js works, except at a TypeScript level.
The text was updated successfully, but these errors were encountered: