You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Star-imported module should conform to the type of a string-indexed object #17622
Conceptually this module represents a map of strings to numbers, { [k: string]: number }, however if I try to use it directly as such I get a type error:
// bar.tsimport*asfoofrom'./foo'functionf(map: {[k: string]: number}){// ...}f(foo)//^^^ Argument of type 'typeof "/Users/tom/code/example/src/foo"' is not assignable to parameter of type '{ [k: string]: number; }'.// Index signature is missing in type 'typeof "/Users/tom/code/example/src/foo"'.
Fortunately there is a workaround: I can simply spread and recompose the module to make it conform to the expected type:
f({ ...foo})// this type checks!
However it'd be nice if the more straightforward approach just worked!
This was just a minimal repro. In practice, the function I'm trying to pass foo to might be defined elsewhere that knows nothing about foo. It might even be in a 3rd party library which I have no control over.
on a side note, this allows for some erroneous coding patterns such as
Indeed. With respect to reporting a bug about the mutable binding via an indirect reference to a module namespace object, the closest approximation is the readonly modifier which currently doesn't do much. It still probably should be part of the type though.
I can't get the workaround from the OP to work with a JS library I'm using:
// foo.d.tsdeclare module "foo"{classBar{ ... }}// app.tsimport*asFoofrom"foo";letf={...Foo};letbar=newf["Bar"];// <-- Flagged with "no index signature" error
I just need a way to pass around class constructors to dynamically choose a subclass at runtime. In my legacy (JS) code I did this with string-indexing into the module -- I'm not married to that solution in TS but it seems like it should work.
Nevermind, it works regardless of the presence of export"foo" if declared in a .d.ts file. Just makes sure that declare module "foo" is in global scope, not module scope as in the latter case it will be interpreted as an augmentation and not a declaration.
I've got the wrong end of the stick here. What I'm trying to do is select a class declared in a module using the class name as a string. What I gave above as an example works without any workarounds, the problem is when I make the index parameter a variable instead of a literal. It's complaining that I can't index into the module with any old string, and I don't know how to constrain it such that the variable is forced to be a valid index key. I thought that was keyof but I can't get that to work either.
A better example:
import*asFoofrom"foo";letb1=newFoo["Bar"];// I was wrong, no compiler error hereletk2: string="Bar";letb2=newFoo[k2];// compiler error, "has no index signature"letk3: keyofFoo="Bar";// compiler error, "Cannot find name 'Foo'."letb3=newFoo[k3];// I think this would work?
Of course I can avoid this by just casting Foo to any but that kind of defeats the point.
We should be able to apply the same treatment of inferred indexers to star-imports as we do for object literals, since (in theory) we know the entire set of members (because ES6 modules are immutable).
Reacted by Aluan Haddad, Kitson Kelly and James Bromwell
Activity
pelotom commentedon Aug 4, 2017
Interestingly, there doesn't seem to be a problem when using a value type of
any(typeof map={ [k: string]: any }).aluanhaddad commentedon Aug 6, 2017
Since imports are inherently static, why not use
on a side note, this allows for some erroneous coding patterns such as
pelotom commentedon Aug 6, 2017
This was just a minimal repro. In practice, the function I'm trying to pass
footo might be defined elsewhere that knows nothing aboutfoo. It might even be in a 3rd party library which I have no control over.Yeah, that's an oddity which should probably get its own bug report.
pelotom commentedon Aug 6, 2017
It seems like when treating
fooas an object it should be no different than if instead of importing a module we had declaredAnd in this case,
type checks just fine.
aluanhaddad commentedon Aug 6, 2017
Indeed. With respect to reporting a bug about the mutable binding via an indirect reference to a module namespace object, the closest approximation is the
readonlymodifier which currently doesn't do much. It still probably should be part of the type though.kitsonk commentedon Aug 6, 2017
Duplicate of #16248
Duplicate of #10998
thw0rted commentedon Aug 17, 2017
I can't get the workaround from the OP to work with a JS library I'm using:
I just need a way to pass around class constructors to dynamically choose a subclass at runtime. In my legacy (JS) code I did this with string-indexing into the module -- I'm not married to that solution in TS but it seems like it should work.
aluanhaddad commentedon Aug 17, 2017
James Bromwell (@thw0rted) just in case, did you forget toexportBar?Nevermind, it works regardless of the presence of
export"foo"if declared in a.d.tsfile. Just makes sure thatdeclare module "foo"is in global scope, not module scope as in the latter case it will be interpreted as an augmentation and not a declaration.thw0rted commentedon Aug 17, 2017
I've got the wrong end of the stick here. What I'm trying to do is select a class declared in a module using the class name as a string. What I gave above as an example works without any workarounds, the problem is when I make the index parameter a variable instead of a literal. It's complaining that I can't index into the module with any old string, and I don't know how to constrain it such that the variable is forced to be a valid index key. I thought that was
keyofbut I can't get that to work either.A better example:
Of course I can avoid this by just casting Foo to
anybut that kind of defeats the point.aluanhaddad commentedon Aug 17, 2017
I see what the issue is now.
needs to be written as
since
keyoftakes a type butFoois a value. In some situations,Foomay also refer to a type but in the example you have shown, it is only a value.For example
RyanCavanaugh commentedon Aug 17, 2017
We should be able to apply the same treatment of inferred indexers to star-imports as we do for object literals, since (in theory) we know the entire set of members (because ES6 modules are immutable).