Closed
Description
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
- But people do love
- 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 '/' {
ordeclare module '' {
- A: We have a special string that means the global scope, use e.g.
- 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
- Add an
- 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
- A: Maybe you just don't... or refer to
- Theory that
interface foo.bar { }
always means extendingbar
from thefoo
namespace- With special LHS semantics for
global
- Can I declare things here, too?
- Yes
- With special LHS semantics for
- 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
import
s allowed in here? No. - Is
export =
allowed in here? No. - Are the containing module's symbols in scope? Yes
- Are module
- Rabble about declaration emitter
- Vlad and Anders to tackle it
- How do I make a class appear to have methods that I don't want to implement