-
Notifications
You must be signed in to change notification settings - Fork 1
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
Applying a function generic from a different context #2
Comments
Hi, No problem, I consider it part of the documentation. People often mention higher kinded types when this use case comes up, but no implementation of HKT in TS can apply a generic function type with an argument dynamically, because the parameters in a generic function are not of the same kind as the parameters in a generic type alias or interface and TS doesn't provide any hook to access them at the type level. However type-lenses may help if you are ready to take a risk. First, let me suggest something simpler that we can do at the value level, because it may be enough for your use case. declare function validate <Request>(req: Request): { validated: Request };
// we can actually do this at the value level
const validateFooString = validate<Paths['/route']['get']['request']>
const api: Api<Paths, typeof validateFooString> = {
before: validate,
api: {
"/route": {
get: (req, before) => {
before.validated
// ^? (property) validated: { foo: string; }
return (before as any).validated.foo
}
}
}
} Now if we want to do this dynamically, an idea is to replace type-lenses require a path to replace some piece of type with another. We can write a utility type that looks for // create a path leading to `unknown` in the object type
type CreatePath<T> = FilterPath<GetPaths<T>>
// get all possible paths and the value they lead to in the object type
type GetPaths<T> = T extends object
? { [K in keyof T]: [K, ...GetPaths<T[K]>] }[keyof T]
: [T];
// exclude paths which don't lead to `unknown`
type FilterPath<T> = T extends [unknown, ...unknown[]]
? IsUnknown<Last<T>> extends true ? Init<T> : never
: never; With this path it is now possible to leverage // in API
before: Replace<CreatePath<ReturnType<B>>, ReturnType<B>, P[path][method]['request']> No change needs to be made to const api: Api<Paths, typeof validate> = {
before: validate,
api: {
"/route": {
get: (req, before) => {
before.validated
// ^? (property) validated: { foo: string; }
return (before as any).validated.foo
}
}
}
} Now the risk I mentioned is that |
First off, seriously thank you so much for taking the time to write out such a lengthly and complete response! 🙇
Unfortunately this isn't sufficient for my use case. In my attempt to make the example code as simple as possible, I removed some critical context. The goal is to get the before response type to work across various routes, based on the request types of each route: It's going to take me a bit to fully grok this, but it definitely seems inline with what I'm looking for! I do have one immediate followup question.... Why, if I replace the return type of validated with something that doesn't use the generic parameter I assume it has something to do with the conditional types used here |
You're welcome. The issue here is that I would compare before: CreatePath<ReturnType<B>> extends infer Path extends Query
? [Path] extends [never]
? ReturnType<B>
: Replace<Path, ReturnType<B>, P[path][method]['request']>
: never Note that you need to import the constraint This reminds me that when using |
Awesome thanks! I've been messing around with I think I enjoy writing types more than the code itself 😅 |
Yeah that's something to be wary of actually ;) By the way, this issue made me consider including path finding to type-lenses, and changing the behaviour of Don't hesitate to post issues if the documentation wasn't very clear. I don't get a lot of feedback. |
type-lenses now implements before: CreatePath<ReturnType<B>> extends infer Path extends Query
? [Path] extends [never]
? ReturnType<B>
: Replace<Path, ReturnType<B>, P[path][method]['request']>
: never can be re-written as: before: FindReplace<ReturnType<B>, unknown, [P[path][method]['request']]>
before: Replace<FindPath<ReturnType<B>, unknown>, ReturnType<B>, P[path][method]['request'], any>
|
I ended up here from this typescript issue. and think this library might solve the problem I'm running into.
I'm trying to create an library interface where a generic function can be passed in and the type basically resolved from a different context:
Hopefully this Typescript-Playground isn't too hard to follow.
Sorry for such a direct question, please close/delete if this isn't appropriate.
The text was updated successfully, but these errors were encountered: