When I say "ergonomic," I mean something that doesn't force users to utilize the syntax of
.InstanceType<typeof mod["Foo"]>
Currently, I am working on a .d.ts file for a JavaScript source file. The plan is to convert this JS file into TypeScript in the future, although it cannot be done at present.
The structure of the file is as follows:
// module.js
class Foo {
sayFoo(){}
}
export default {
Foo
};
With 'tsc,' the generated .d.ts looks like this:
// module.d.ts
declare class Foo {
sayFoo(): void;
}
declare const _default: {
Foo: typeof Foo;
};
export default _default;
The user has to go through some convoluted steps to access the class type:
import mod from "./module"
function takesFoo(foo: InstanceType<typeof mod["Foo"]>) {
foo.sayFoo();
}
However, if I slightly manipulate the types using namespace
:
export default NS;
// module.d.ts
declare namespace NS {
export class Foo {
sayFoo(): void
}
}
then the user gets a much smoother interface at the type level!
import mod from "./module";
function takesFoo(foo: mod.Foo) {
foo.sayFoo();
}
I could opt for using namespace
, but then we'll face issues when the JS file is eventually converted to TS. Our options at that point would be:
(A) Avoid converting the library's entry point to TS altogether. Develop most of the library in TS and use a small JS file with a .d.ts to package the API. This approach would eliminate any assurances that we are adhering to our public API! Furthermore, it could lead to confusion.
(B) Enforce the complex type-level API onto users. Perhaps mitigate this by exporting certain interfaces and educating users about the dual nature of TS class types.
Is there another option I may have overlooked or misunderstood regarding type-level features in TS?