Skip to content

Embedding types in module/namespace declarations #35030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
5 tasks done
alshdavid opened this issue Nov 11, 2019 · 6 comments
Closed
5 tasks done

Embedding types in module/namespace declarations #35030

alshdavid opened this issue Nov 11, 2019 · 6 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@alshdavid
Copy link

alshdavid commented Nov 11, 2019

Search Terms

module namespace declaration rexporting

Suggestion

When trying to export types in an index.ts, currently one must redefine those types in the barrel.

e.g.

Consumer

import { myModule } from 'my-module'

function foo(bar: myModule.MyClass) {

}

MyModule

Currently developers must export their variables and export their types too, but types must be redeclared.

// my-module/index.ts
import * as myClass from './my-class';

export const myModule = {
  ...myClass
} 

export declare namespace myModule {
  export type MyClass<T> = myClass.MyClass<T>
}

Suggestion

Would be nice if developers could just embed the type in the namespace

// my-module/index.ts
import * as myClass from './my-class';

export const myModule = {
 ...myClass
} 

export declare namespace myModule {
  export types from './my-class'
}

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@alshdavid alshdavid changed the title Embedding types in module declarations Embedding types in namespace declarations Nov 11, 2019
@alshdavid alshdavid changed the title Embedding types in namespace declarations Embedding types in module/namespace declarations Dec 8, 2019
@andrewbranch
Copy link
Member

andrewbranch commented Dec 10, 2019

It looks like what you really want is

import * as myClass from './my-class';
export { myClass as myModule };

Which, with #4813 / #34903, will be expressible as a one-liner as

export * as myModule from './my-class';

Is that a fair representation of your end goal?

@andrewbranch andrewbranch added the Question An issue which isn't directly actionable in code label Dec 10, 2019
@alshdavid
Copy link
Author

alshdavid commented Dec 11, 2019

No because I will often collect multiple files and export them under a single package namespace

import * as a from './a'
import * as b from './b'
import * as c from './c'

export const myLibrary = {
   ...a,
   ...b,
   ...c,
}

export default myLibrary

export declare namespace myLibrary {
   export type A<T = any> = a.A<T>
   export type B = b.B
   export type C = c.C
}

making the contents accessible against the namespace

import myLibrary from 'my-library'

function foo(bar: myLibrary.A) {
    console.log(bar)
}

const a = new myLibrary.A()
foo(a)

This can get annoying and error prone if you have many types

export declare namespace myLibrary {
   export type A<T = any> = a.A<T>
   export type B = b.B
   export type B2 = b.B2
   export type C = c.C
   export type C2<T1 = string, T2 = number, T3 = any> = c.C2<T1, T2, T3>
   // etc
}

@andrewbranch
Copy link
Member

At least you can avoid redeclaring type parameters:

declare namespace myLibrary {
  export import A = a.A;
  // ...
}

If you didn’t need it to be a default export, it would just be

export * from './a';
export * from './b';
export * from './c';

and

import * as myLibrary from 'my-library'

And probably a less error-prone workaround that maintains the default export is to consolidate in another file:

// barrel.ts
export * from './a'
export * from './b'
export * from './c'
// index.ts
import * as myLibrary from './barrel'
export default myLibrary

But yeah, I guess there’s no 100% kludge-free way to do what you’re trying to do. But even if there was, I would personally not be a fan of a module structure like this as an API consumer. By barreling everything up and reexporting as a default export, you prevent consumers from doing named imports of individual types:

image

@andrewbranch andrewbranch added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript and removed Question An issue which isn't directly actionable in code labels Dec 12, 2019
@alshdavid
Copy link
Author

alshdavid commented Dec 12, 2019

Hey nice! That's a clever work around!

Just to extend it to enable named exports:

// index.ts
import * as myLib from './barrel'

export {myLib}
export default myLib

This can be used both like

import myLib from 'my-lib'

and

import { myLib } from 'my-lib'

@andrewbranch
Copy link
Member

My point about named imports isn’t about importing myLib as a named import, but about reaching in and pulling out an export from module a.ts. I never want to have to write new myLib.A(), I want to be able to write import { A } from 'my-library'; new A().

@alshdavid
Copy link
Author

I used to feel the same way about imports - always prefer picking out named exports from a library.

I started working with Go (Golang) a few years back. Go is syntactically similar to TypeScript and something that really stuck out was the ergonomics of grouping logic into "packages".

It sounds stupid but professionally, it has resulted in significant improvements of code quality.

It's unconventional for JS/TS, but I am happy that the language is flexible enough to allow for this coding style.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

2 participants