TypeScript Version: 2.0.6(VSCode 1.7.1 included)
Code
test.ts
const abc = {};
function foo(){}
namespace foo {
export let abc = abc; // <- [ts] Block-scoped variable 'abc' used before its declaration.
}
foo.abc = abc; // <- here is the right reference to `const abc = {}`
Expected behavior:
In namespace foo, I can assign outside abc to namespace foo
Actual behavior:

Error: [ts] Block-scoped variable 'abc' used before its declaration.
And if I use foo.abc = abc, the right hand abc is correct reference for const abc = {}
Is it designed?
The only way to do it right now is
const abc = {}
function foo(){}
namespace foo {
export let abc;
export let def = 'def';
}
foo.abc = abc;
But it's not clear. I don't know why def can define and assign together, but abc can't.
The behavior reproduces with just this:
let abc = abc;
The problem in your full example is let abc = ... creates a new variable in the scope of foo -- namespaces create new scopes in TypeScript. It shadows, or overrides, the abc from the outer scope. So it thinks you're trying to initialize a variable with itself. It has no idea there's another abc you're trying to reference above it.
Initializing a value with itself is allowed with var, yielding undefined, but throws an error with block-scoped variables, both in the compiler and in a JavaScript runtime.
@jwbay I just want to do this in javascript
export let abc = {};
function foo(){}
foo.abc = abc;
export foo;
If I do not use namepspace in typescript, the type chcker will throw a error/warning:
[ts] Property 'abc' does not exist on type 'typeof foo'.
And a new problem
const abc = {};
function foo(){}
namespace foo {
export let abc;
export let def = 'def';
}
foo.abc = abc;
will emit javascript
var abc = {};
function foo() { }
var foo;
(function (foo) {
foo.def = 'def';
})(foo || (foo = {}));
foo.abc = abc;
ESLint throw the error [eslint] 'foo' is already defined. (no-redeclare). Why function foo and var foo both emitted here?
A further more question.
If I want to assign a property to a function, but the function is not top level. Then the namespace usage will lead to error [ts] A namespace declaration is only allowed in a namespace or module..
How can I assign a property to a function in this condition?
function a(m, n){
function b(p){
b.p = p
}
b.m = m;
return b;
}
interface callable {
(x: number): number;
property: string;
}
let bar: callable;
bar(0); //ok
bar.property = 'x'; //ok
If you just want the property on the function and don't care about accessing it later with type safety, you can cast to any and attach it.
function foo() { }
(foo as any).asdf = 42;
To your last example, this should be close. It uses a type with a call signature.
function a<M>(m: M) {
interface returnType {
(p: any): void;
p?: any;
m: M
}
let b = function (p) {
b.p = p;
} as returnType;
b.m = m;
return b;
}
let foo = a(42);
foo.m === 42;
foo('');
foo.p === '';
Thanks very much for your patient and detailed answer help me to understand TypeScript more. 馃槃
Most helpful comment
If you just want the property on the function and don't care about accessing it later with type safety, you can cast to any and attach it.
To your last example, this should be close. It uses a type with a call signature.