N.B. these notes use the word "module" extensively, which we now use to only mean "external module" in the old parlance. I will use "namespace" to refer to "internal modules" if needed.
Augmenting Types from External Modules (#5269, #4166)
- Bind operator is like extension methods for JS
- But people do love
.
- And if you love
.., prototype mutation is the only way to accomplish it
- Two problems facing Rx
- How do I make a class appear to have methods that I don't want to implement
- Remove the check for ambientness of classes merged with interfaces (:+1:)
- How do I make the type of a global (or module-scoped) type depend on the set of modules that are imported?
- This is an anti-pattern, but what can you do ¯_(ツ)_/¯
- Some very terrible workarounds exist
- Implementation approach
- Binder has a concept of 'scripts' which contribute to the global, and 'modules' which contribute to their local scope, along with their separate exports
- Only ambient modules have a symbol in the global scope, which is their quoted name
- Module files never get a symbol and are not subject to merging; this is what we would need to change
- Fix: add a canonical name to all module files so that they become mergable
- This gets us augmentation of modules even from within other modules, which is currently disallowed
- Q: How do you augment global types in this model?
- A: We have a special string that means the global scope, use e.g.
declare module '/' { or declare module '' {
- Note that the types in a module would not directly visible in an interior ambient module declaration
- Add an
import declaration? It's weird. Maybe implicitly import them into scope... try it out and see what happens, should be OK
- Q: How do we resolve these names?
- A: Relative to the current file
- Q: How do we produce these canonical names?
- A: Need to figure that out. Doable.
- Q: How do I refer to shadowed global types from within an ambient module inside a module?
- A: Maybe you just don't... or refer to
"./obs".Observable
- A: Or `import { Observable } from './obs';
- A: Or
global.Array in the global case?
- A: Or
interface obs.Observable<T> { ... } <- nice
- Theory that
interface foo.bar { } always means extending bar from the foo namespace
- With special LHS semantics for
global
- Can I declare things here, too?
- Syntax/semantics:
interface global::Foo, interface global.Foo, interface global Foo, ?
declare global var x;
declare in './obs' var x;
- What happens if I try to
declare in a non-existent module?
- It's like an ambient external module?
- Only ambient classes could use dotted declarations
- Speculation that we could emit non-ambient classes this way
- 👍 Conclusions
- Allow merging of interfaces and non-ambient classes
declare module 'foo' { is allowed in modules
- Are the containing module's symbols in scope? Yes, and the global symbols have priority
declare module '../relativePath' { is allowed in a module, but not in a script
- Are the containing module's symbols in scope? Yes
declare global { augments the gloal scope, and is only allowed at top-level in a module (not a script)
- Are module
imports allowed in here? No.
- Is
export = allowed in here? No.
- Are the containing module's symbols in scope? Yes
- Rabble about declaration emitter
- Vlad and Anders to tackle it
N.B. these notes use the word "module" extensively, which we now use to only mean "external module" in the old parlance. I will use "namespace" to refer to "internal modules" if needed.
Augmenting Types from External Modules (#5269, #4166)
..., prototype mutation is the only way to accomplish itdeclare module '/' {ordeclare module '' {importdeclaration? It's weird. Maybe implicitly import them into scope... try it out and see what happens, should be OK"./obs".Observableglobal.Arrayin the global case?interface obs.Observable<T> { ... }<- niceinterface foo.bar { }always means extendingbarfrom thefoonamespaceglobalinterface global::Foo,interface global.Foo,interface global Foo, ?declare global var x;declare in './obs' var x;declare ina non-existent module?declare module 'foo' {is allowed in modulesdeclare module '../relativePath' {is allowed in a module, but not in a scriptdeclare global {augments the gloal scope, and is only allowed at top-level in a module (not a script)imports allowed in here? No.export =allowed in here? No.