Skip to content
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

#307 Assign type of any to asMap and asMappedResults #308

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

bbahrman
Copy link

@bbahrman bbahrman commented Sep 18, 2024

Assigning type of any to asMap and asMappedResults allows for them to be assigned to meaningful interfaces/types

See: #307

@ShawnTalbert
Copy link
Contributor

I would suggest we make this a generic so the caller can directly provide a return type instead of introducing 'any'. Thoughts?

@darrenhillconsulting
Copy link
Contributor

I agree @ShawnTalbert , I've been meaning to do this. @bbahrman , let me know if you need a hand converting this to use Generics.

@bbahrman
Copy link
Author

@ShawnTalbert @darrenhillconsulting - Thanks for the suggestion to use a generic, new to me, sorry it took a bit for me to get myself understanding what you were trying to do with it. Thanks Darren for the example. I think I just copied and pasted your example in, but wanted to test it out to be sure I understood the why (I trust you, but like to learn).

Do either of you feel the need to have the generic type be a named type in some way? I don't think it does, but let me know if you want any further changes.

Copy link
Contributor

@darrenhillconsulting darrenhillconsulting left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe remove unrelated changes? Like the JSDoc changes?

@bbahrman
Copy link
Author

Maybe remove unrelated changes? Like the JSDoc changes?

@darrenhillconsulting fair point, sorry those were some errant whitespace changes. I've cleaned them up.

Copy link
Contributor

@darrenhillconsulting darrenhillconsulting left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this change. @ShawnTalbert ?

@darrenhillconsulting
Copy link
Contributor

@MrRob , what's your thoughts on this one?

@ShawnTalbert
Copy link
Contributor

ShawnTalbert commented Sep 26, 2024

Ideally, it sounds like the user would see the return value as simply type T[], having it be LowercaseKeys<T>[] or even more confusing, nested LowercaseKeys<LowercaseKeys<T>>[] isn't a great user experience. Can we do better before we unleash it on the world?

image

@MrRob
Copy link
Collaborator

MrRob commented Oct 22, 2024

I'm still confused on how this works. Can someone record a quick Loom demo? Or, I've updated the example-UserEventScript.ts to have a query in it, the old ("as any") way. Can we update that with an example in this PR?

@bbahrman
Copy link
Author

@MrRob https://www.loom.com/share/96938eb830ec4031aa9ccd28bbe020e0

I'm still trying to figure out a solution for @ShawnTalbert 's question. I tried just renaming LowerCaseKeys back to QueryResultMap. I tried a bunch of other variations but couldn't find a cleaner option that also maintained the validations.

In the process I also added in some result validation. Queries don't return booleans which the previous type allowed, results will be string, number, or null.

@ShawnTalbert
Copy link
Contributor

ShawnTalbert commented Oct 22, 2024

Try something like

export type QueryMapResult<T> = {
    [K in keyof T as Lowercase<K & string>]: T[K]
};

which should provide an experience something like this in the IDE:
image

Note that the CTRL+Q popup shows the uppercase foo from the original type; as long as we're ok with that, this type enforces the lowercase keys in a single (not nested) return type for asMappedResults().

I wonder though, are the lowercase shenanigans worth it? Instead of type magic to convert the keys to lowercase, why not force the user supplied type to actually have lowercase keys? I mean, why encourage or support a user passing a type with uppercase keys as I show in that screenshot at all? Instead, force them to provide a type that contains lowercase keys only?

@ShawnTalbert
Copy link
Contributor

Actually, that constraint seems to hold already - I should have just had my vanilla defined type - not involving QueryMapResultexplicitly.
For example this seems to satisfy the compiler:

    let r: ResultSet = {} as ResultSet
    interface F { foo:number, bar:string, baz:string }
    const mapped = r.asMappedResults<F>()
    if (mapped[0].foo === 2)

but trying to sneak in an uppercase Bar will be rejected:
image

So maybe the modification I suggested above for QueryMapResult is adequate?

@ShawnTalbert
Copy link
Contributor

@MrRob https://www.loom.com/share/96938eb830ec4031aa9ccd28bbe020e0

I'm still trying to figure out a solution for @ShawnTalbert 's question. I tried just renaming LowerCaseKeys back to QueryResultMap. I tried a bunch of other variations but couldn't find a cleaner option that also maintained the validations.

In the process I also added in some result validation. Queries don't return booleans which the previous type allowed, results will be string, number, or null.

@bbahrman , the small change from my replies about is to be sure we type each key to match the type in the user-provided generic - otherwise every key had type string | number | null rather than having the types of individual keys passed as T. From my examples above, the keys foo, bar, baz have distinct concrete types of number, string,string respectively - that should hopefully be reflected in the output after mapping, no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants