-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Provide a way to augment global interfaces from external modules #4166
Comments
Am I being too ignorant, but why wouldn't the following be acceptable? interface global.Window {
myPlugin: any;
}
let x = window.myPlugin;
export class Foo { } My particular use case was extending other global interfaces within a module that was going to try to offload to native if present: interface ObjectConstructor {
assign(target: any, ...sources: any[]): any;
}
const assign = 'assign' in Object ? Object.assign : function assign(target: any, ...sources: any[]): any {
// shim...
}
export default assign; |
Too bad there is no [discussion] tag, gonna add my few cents anyway. It's shocking how a big new feature is coming out of a tiny problem: to get something out of a standard object that is not supposed to be there. If so, what could be easier than this: export function toSomethingCrazy(window: Window) : SomethingCrazy {
return (<any> window).stupidPlugin;
} That's it, the problem has gone. No need for a new expensive feature. Thank to one single guarded place in code. |
@Aleksey-Bykov that does not allow for modeling global side-effects. a library author would want to declare that their library is adding certain definitions to the global scope, so that their users get to use them. for context, we have rejected this in the past on the basis of discouraging bad practices. however, like them or not, there are existing JS libraries that relay on global extensions to work. the only way to do this today in TS, is to define your global polluters in a .d.ts file, then add a /// reference to it from your module. |
well, can't say you have any other choice other than to add it |
It isn't just global polluters... The other use case is trying to extend the globals without having to load the full |
@kitsonk can you elaborate.. |
@kitsonk, you sound like such pollutions would be localized in a module where they are introduced (rather than leaking to the global namespace) which sounds opposite to what @RyanCavanaugh originally said |
All that is being suggested is that there is a language mechanism to access global interfaces from within a module. The reasons for it were originally thought to be useless which is why #983 got closed, then re-opened, and now closed again in lieu of this one. There can be many use cases, like having to do with other libraries that pollute the global namespace, which is bad, or my particular use case which is that I wanted to "shim" the global interfaces so I could better handle when the native ES6 functionality wasn't there. So one is the living in the sad reality of the wild west of the web and one might be actually a good thing, though it was better in the end for my use case to extend the global interface (#3889). |
Will this help fix code like
I know there's a workaround where I could have
as the first line in frobulatorData.ts, but then I've got Frobulator.Frob and Frobulator_Frob in my global scope which is a bit untidy. If I have many interfaces in my global Frobulator namespace then I've got a lot of duplication to do. I can fix this by changing some namespaces of course but global:: would be very handy. As I said in another discussion on this topic, the generated JavaScript is easy in this case - there is none since I'm just worrying about interfaces. I appreciate this would be more difficult with classes but not impossible (some auto-generated type aliases, with some sort of consistent name mangling, at the start of each ts file's emit would do the trick) Note: I'm just letting VS 2015 build separate *.js files and using ASP.Net bundling to get them on to my page - no ES6 modules, gulp tasks, requireJS or anything like that. |
I wish we had a "pimping" mechanism à la Scala (http://www.artima.com/weblogs/viewpost.jsp?thread=179766). An oversimplified view is that when an implicit converter exists between type A and type B, methods of type B can be called on type A (implicit conversion happens in the background). What is great about this mechanism is that it avoids monkey patching and that the conversion only happens when a converter is "in scope/visible" (so conversion does not have to be global). It is also super generic and can be applied to any type. Implicits are a whole subject in itself, but at least with Typescript if we could have a way of "registering" a converter on a type, that would be great. |
Were the referenced design notes #5292 fleshed out as a proposal? |
PR #6213, should already be in master and available in our nightly builds ( |
I think I have this problem. I have a d.ts file that I want to extend global with. This works fine: interface Window {
foo:number
}
declare module 'bar' {
export const baz:string;
} But I'd like to import a type for the Window augmentation: import SomeClass from './somewhere'
interface Window {
foo:SomeClass
}
declare module 'bar' {
export const baz:string;
} But then I get something like 49 declare module 'bar' {
~~~~~~~~
src/externals.d.ts(49,16): error TS2665: Invalid module name in augmentation. Module 'bar' resolves to an untyped module at '/path/to/bar.js', which cannot be augmented. with no clear indication what the problem was, until I found the link in @mhegazy's comment #9748 (comment). It seems not possible to import something into a non-module declaration file that has ambient modules in order to make a type definition? What's the solution? Do I need to make a separate module declaration file and keep ambient stuff in a second non-module declaration file? |
Ah, yep, that worked. I moved the Window type augmentation into an external module and made it the first import in my entry point, and the other ambient stuff is still in the non-module .d.ts file. |
We get "bug reports" of this nature very often:
Our current answer today is "Move your stuff to another .d.ts file". This is an OK workaround, but not great. The real sticker is when there's a type defined inside an external module -- it's impossible to use those types to augment a global interface, even though this is a thing that actually happens. It gets even trickier because this encourages people to move types into the global namespace when they didn't want to in the first place, exacerbating the problem.
We need some proposal for how to make this work.
The text was updated successfully, but these errors were encountered: