-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Expose inferred type for use in type annotations #33480
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
Comments
Can you elaborate on this a bit? I was imagining a pretty simple process, which I think would work for all the examples above:
Yeah, #32738 looks like a duplicate...but since that was closed without a proposal, hopefully we can continue the discussion here. I don't really see the connection to #7481 (but haven't read that whole thread); I did search #7481's thread for the the |
When the annotated type is a mapped type, it's reasonably straightforward for a human to puzzle it out. For pretty much anything else, it's incredibly unclear. For example, what if the type is a conditional type that could provide a contextual type? type Magic<T> = T extends (n: number) => void ? (n: string) => void : (n: number) => void;
// What is the type of p, and the type of k?
let p: Magic<inferred> = k => { };
This isn't going to work very well. You wouldn't expect an implicit any on type HasFoo<T> = T & { foo?: any };
const f: (n: number) => any & HasFoo<inferred> = m => {}; |
Honestly, I think I would be fine with an implicit any on const f: HasFoo<inferred> = (m: number) => {}; With that rule, your conditional type example resolves to That said, maybe a more sophisticated rule could be:
I think that'd make your Alternatively, other procedures could be used for converting the LHS annotation with |
Normally I would not have bothered to post, since I have already found a work-around, but you seem to care a lot about the language and trying to make it more capable and suitable for enterprise use. So I decided to invest a lunch in sharing my experiences in the hope that it might help you. Summary:Exposing inferred type for use in type annotations is completely necessary and it is a tragedy that Typescript lacks this feature. In my experience, the vast majority of the time team-mates use an index type they actually want to refer to inferred type, but can't due to Typescript limitations. I am close to banning index types in our projects due to this. Because: const myConstantsObject = {
'KEY' : {a: 1},
'VALID-KEY-2' : {a: 2}
} Works as expected, compiles, offers vscode IDE completion and throws types error (for example that But as soon as someone tries to create a type to make sure nobody adds entries of the wrong type: const myConstantsObject:{ [key: string]: {a: number} } = {
'KEY' : {a: 1},
'VALID-KEY-2' : {a: 2}
} Then suddenly Were I to my own post without knowing anything about this, I would suggest an enum or some other workaround that would not have worked in real life, and believe there is no real use case for inferred reference. Hence, I must unfortunately share a more complicated explanation closer to real-life problem we had, which will inevitably turn this reply into a wall of text. In-depth context:I wrote types for a working Javascript solution and found out that Typescript is currently not capable due to lack of inferred type reference. Here is a simplified version of the real problem that originally lead me to this issue: const objectOfHOFs = {
fillInMissing : (objectOfHOFs) => (fnArgs) => ({...pass(objectOfHOFs), ...fnArgs}),
overwrite : (objectOfHOFs) => (fnArgs) => ({...pass(objectOfHOFs), ...hofArgs}),
/** dozens more... **/
} Where function <A extends { [key: string]: (...args: any) => any }>pass(objectOfHOFs: A) {
const objectOfFunctions = {} as { [K in keyof A]: ReturnType<A[K]> }
for (const [key, hof] of Object.entries(objectOfHOFs)) {
act[key as keyof A] = hof(objectOfHOFs)
}
return objectOfFunctions
} Resulting in an object of regular (i.e. non-higher-order) functions with the same keys. Everything works exactly as it should... Until you turn on the implicit any flag, that is, because function arguments are implicit Ideally we would want to add a type to the object of higher order functions (objectOfHOFs) rather than manually have to type every single key in the entire object higher order functions for every single argument i.e: const objectOfHOFs: {[K in keyof inferred]: (hofArgs: inferred) => (fnArgs: argType) => ReturnType<A[K]> } = {
fillInMissing : (objectOfHOFs) => (fnArgs) => ({...fnArgs, ...pass(objectOfHOFs)}),
overwrite : (objectOfHOFs) => (fnArgs) => ({...pass(objectOfHOFs), ...fnArgs}),
/* dozens more... */
} // ✅ Concise and everything is typesafe This would be possible and easy to do if inferred type could be referenced. Currently, however, one would have to do something like this: const objectOfHOFs = {
fillInMissing : (objectOfHOFs: { fillInMissing: => (fnArgs: argType) => ManuallyWrittenReturnType , overwrite: (fnArgs: argType) => ManuallyWrittenReturnType2 /* dozens more... */ }) => (fnArgs: argType) => ({...fnArgs, ...pass(objectOfHOFs)}),
overwrite : (objectOfHOFs: { fillInMissing: => (fnArgs: argType) => ManuallyWrittenReturnType , overwrite: (fnArgs: argType) => ManuallyWrittenReturnType2 /* dozens more... */ }) => (fnArgs: argType) => ({...pass(objectOfHOFs), ...fnArgs}),
/** dozens more... **/
} // ❌ It “works”, but it is a lot of manual effort, very verbose, still type unsafe and entries for the wrong type can still be added. Typescript does not usually let you write self referential generic types. However, I realised I that I could circumvent this limitation by writing a helper function with a generic argument type. Nowadays, whenever I need to type anything relying on inferred type I am now forced to write a completely unnecessary function with a complex generic type and confuse the life out of everyone in our team: const objectOfHOFs = typeAsObjectOfHOFs({
fillInMissing : (objectOfHOFs) => (fnArgs) => ({...fnArgs, ...pass(objectOfHOFs)}),
overwrite : (objectOfHOFs) => (fnArgs) => ({...pass(objectOfHOFs), ...fnArgs}),
/* dozens more... */
}) // 😖 It works and it is typesafe but it is a hack that confuses my coworkers. I hope you agree with me that 1) this is a hack, 2) that adding unnecessary implementation to circumvent the limitations of the typing system is not an ideal solution, and 3) that something should be done about this. If you have any questions or want me to put up a minimal code-sandbox with a minimal reproduction (or the hacky function work-around) don't hesitate to ask. |
Search Terms
infer inferred type annotation
Suggestion
When annotating the type of a variable, allow the variable to have access to the type that would've been inferred for the RHS, so that the author can build a type annation derived from that inferred value. This value could be exposed as the
inferred
keyword or similar.Use Cases/Examples
Related issues
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: