镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Typing the Index Signature of a Module Namespace Object #10998

Description

Hi. I have a series of related classes packaged up and exported from a single module. Something like:

// states.ts

export {default as Alabama} from "./alabama";
export {default as Alaska} from "./alaska";
//...

Now, elsewhere in the code, I want to iterate over all the related classes:

import * as States from "./states.ts"

for(let stateExportName in States) {
  let thisState = States[stateExportName];
  // ... do something.
}

Hold aside that iteration over a commonjs module's export names would use a for-in, whereas iterating over an es6 module namespace object's exports would use a for-of (since es6 does define @Iterator (@iterator) on module namespace objects)... in either case, this iteration should be possible.

I'd like to be able to provide a type for the index signature of the module namespace object, so that thisState is automatically inferred as a State. I asked on StackOverflow, and Basarat Ali Syed (@basarat) said that wasn't possible, so I'm opening an issue here for it as a potential feature request. It does seem like an edge case, so I'm not sure how worth solving it is, but it is something I bumped into.

The other thing is that, if I use the code as written above, I get an "Index signature of object type implicitly has an 'any' type" error. That makes sense, since I haven't defined an index signature for the module anywhere. However, I get that error even if I explicitly type the thisState variable as any like so:

for(let stateExportName in States) {
  let thisState: any = States[stateExportName];
  // ... do something.
}

I can't tell if that's a bug or expected behavior. If it is expected behavior, though, then I have to do some trickery to silence the compiler error...which is really annoying (aesthetically—but also because having any compiler errors seems to prevent tslint from running with type-checking on).

Activity

  1. mhegazy commented on Sep 19, 2016

    @mhegazy
    Contributor

    possibly something like the prosal in #420 could cover this.

    Another possibility is using something like keysof operator (#10425) to:

    let stateExportName : keysof States;
    for(stateExportName  in States) {
      let thisState = States[stateExportName]; // typeof States.Alabama | States.Alaska
      // ... do something.
    }
  2. RyanCavanaugh commented on Sep 19, 2016

    @RyanCavanaugh
    Member

    To remove the implicit any error, what you want to write is

      let thisState = (<any>States)[stateExportName];
  3. ethanresnick commented on Sep 20, 2016

    @ethanresnick
    ContributorAuthor

    Another possibility is using something like keysof operator

    Ooh, that seems cool. And maybe keysof States could be automatically inferred as the type in the for-in loop? (I can see why inferring keysof arg might not work in the general case for for-in loops, because of properties up the prototype chain...but it does seem like it should work for module namespace objects, since I'm pretty sure their keys are only own keys, and those can be known statically.)

    To remove the implicit any error, what you want to write is

    Thanks Ryan Cavanaugh (@RyanCavanaugh)! That's the one permutation I didn't try :P

  4. added
    SuggestionAn idea for TypeScript
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    on Sep 23, 2016
  5. kitsonk commented on Jul 26, 2017

    @kitsonk
    Contributor

    Ran across a similiar problem where some sort of inferrance of the index signature would be great. Trying to "rollup" some modules into a larger map is currently not possible.

    Here is an example of the problem we are facing. Given a.ts:

    export const foo = 'foo';
    export const bar = 'bar';
    export const baz = 'baz';

    And then in b.ts:

    import * as a from './a';
    
    const qat: { [key: string]: { [key: string]: string } } = {
      a
    };

    Produces Index signature is missing in type 'typeof "a"'. and I can't figure out an easy way around it, even though TypeScript could infer an index type for the import. The only sort of way that would work would be to cast to any before casting to an index type, but that is type abuse, and I hate being cruel to types.

  6. kitsonk commented on Aug 18, 2017

    @kitsonk
    Contributor

    For sanity purposes, as it seems there are at least 3 open issues on this, Ryan Cavanaugh (@RyanCavanaugh) indicates that an implicit index type can be inferred.

  7. RyanCavanaugh commented on Jun 23, 2021

    @RyanCavanaugh
    Member

    This is possible today if the indexing type is keyof the containing object. Inferring an index signature would be problematic due to it not being legal to write to additional new keys, so I think the current behavior is what we want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.SuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions