I don't understand the tsc
compilation process with respect to<reference>
and splitting the classes of a namespace across multiple files. I'm using the pattern:
src/base.ts
namespace MyNameSpace {
export class Base {
/** constructor, etc. */
}
}
src/subclass.ts
/// <reference path="./base.ts" />
namespace MyNameSpace {
export class Subclass {
/** constructor, etc. */
}
}
This trivial example works. But as I use the pattern on more files, I periodically get the error TypeScript error TS2449: Class 'x' used before its declaration.
.
Are we back to manually managing declaration orderings, like .c
/.h
files? If so, what tools will help be back track the reference to find the error?
My aim to have a manageable set of files that compile into a single .js
and .d.ts
(for another TypeScript library to use). If there a better solution for this scenario? Attempts with variations of Webpack and import
s have failed me.
If it matters, I'm also using nested namespaces, where the members are defined in subdirectories. I'm always using relative reference
paths.
This is very similar to typescript Base type 'xxxx' is referenced before its declaration, which dealt with a module split across multiple files.
Namespaces are not preferred over modules. This is a very important point. Let's dive into the history...
Namespaces used to be declared like this:
module MyNamespace {
}
But they are now declared like this:
namespace MyNamespace {
}
When declaring a namespace, the namespace
keyword is preferred to the old module
keyword.
The reason the module
keyword was replaced with namespace
is that people confused them with "modules". You can see where the confusion might come from! Now actual modules (i.e. files that export or import something) are actually a bit better than namespaces, which is why I have spent the entire answer so far covering all this.
With this in mind, you might consider using modules. Each module gives you a naming context and keeps stuff out of the global scope. You can import and export and everything should work as expected (and you can still bundle it if you like).
src/base.ts
export class Base {
}
src/subclass.ts
import * as Example from './subclass.ts';
export class Base extends Example.Base {
}
If you want to stick with namespaces, you have to respect that everything you place within a namespace (even when that namespace is spread across multiple files) contributes to a single naming context... so you'll have to name the subclass differently.
src/base.ts
namespace MyNameSpace {
export class Base {
/** constructor, etc. */
}
}
src/subclass.ts
/// <reference path="./base.ts" />
namespace MyNameSpace {
export class Sub extends Base {
/** constructor, etc. */
}
}
So ultimately, you can choose modules (proper external modules) where each file has its own naming context, and you can easily avoid naming collisions - or namsepaces where you'll need to be aware that each namespace is a single naming context.
In may examples I have inherited Base
in the subclass for illustration. You're question didn't exactly do that, but I wanted to illustrate the concept. The question had two classes within the namespace with the same name, which is a duplicate name conflict.
I was able to resolve this error by restarting Visual Studio as mentioned in the comments by @Quentin2
typescript some time work find, some time not, and this error come again.
//file B.ts
class B {}
//file A.ts
class A extends B{}
error TS2449: Class 'B' used before its declaration.
If I change file A.ts to B.ts, A.ts to B.ts, then the problem fixed, haha!
I think it is because A is smaller than B, at ascii, the typescript compiler check them by file name. It is so freak! It's not smart enough :(
Or we must use module....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With