#Extensible parameters of method via generics

23 messages · Page 1 of 1 (latest)

sharp locust
#

Let's say I have method get as part of class

/** @type {Get<{ someCustomProperty: string }>} */
get({ headers, params, data, someCustomProperty }) {
    this.get(path, headers, params, data, (data) => {
         // some custom logic where I will use someCustomProperty
    });
}

Then I have type Get.

import { ReqData, ReqHeaders, ReqParams, RespData } from "somewhere";
export type Clients = {
     get<T extends { [key: string]: unknown } = {}>: (args: {
        headers: ReqHeaders,
        params: ReqParams,
        data: ReqData,
     } & T) => RespData;
}

Something like this, but this doesn't actually work. How can I make it so user, who imports my type (from my lib), would be able to use my type both as Get and Get<{ someCustomProperty: string }> ? E.g. extending my own type viac generic?

sharp locust
#

I tried multiple different syntaxes and always end up with errors like:

'(' expected. ts(1005)

in type definition or

Expression expected. ts(1109)

when using the type and tried to call it with generic parameter.

I need user to be able to use it as Clients["get"]<{ o: string }> for example, but I don't want it to be interface if possible.

toxic summit
#

you just need to use method syntax

export type Clients = {
     get<T extends { [key: string]: unknown } = {}>(args: {
        headers: ReqHeaders,
        params: ReqParams,
        data: ReqData,
     } & T): RespData;
}
sharp locust
#

I tried that, but with no success. I tried both syntaxes and same error, Expression expected. ts(1109)

peak sluiceBOT
#
mlocik97#0

Preview:```ts
type ReqHeaders = {};
type ReqParams = {};
type ReqData = {};
type RespData = {};

type Clients = {
get<T extends { [key: string]: unknown } = {}>(args: {
headers: ReqHeaders,
params: ReqParams,
data: ReqData,
} & T): RespData;
...```

toxic summit
#

mm, that's not possible

sharp locust
#

See playground.

toxic summit
#

i don't know what you want me to see, you can't supply type parameters with indexed access, only qualified names

sharp locust
#

Yeah, but even if I do:

type MyType = Clients["get"];
type MyExtendedType = MyType<{ o: string }>

it doesn't work. It really isn't possible to have object with generic functions?

sharp locust
#

I'm still thinking about how to handle this. Because I have OpenAPI spec, and it's structure is:

Paths:
    "/endpoint":
            get: operation["endpoint_operationId"]

And it's not possible to define function with name /endpoint - GET , but such name can be name of PropertyKey. Because I'm practically importing .yaml to .d.ts

#
peak sluiceBOT
#

@sharp locust Here's a shortened URL of your playground link! You can remove the full link from your message.

mlocik97#0

Preview:```ts
type ReqHeaders = {}
type ReqParams = {}
type ReqData = {}
type RespData = {}

declare namespace Clients {
export type get<
T extends {[key: string]: unknown} = {}

= (
args: {
headers: ReqHeaders
params: ReqParams
data: ReqData
...```

sharp locust
#

Another idea I've got is to create multiple .d.ts files with path equal to endpoint path, and then do something like:

import type { get } from "./types/~/path";

export type MyType = get<{ o: string }>

For /path endpoint. 🤔

#

Actually, thinking more and more about it, this may be actually really good idea.

#

And resp then I can have:

export type MyType = import("/path").get<{ o: string }>
#

Using Vite to make path alias and ... yeah. I guess I solved this. Autocomplete and Intellisense should also be fine. This API design looks good.

#

!solved

sharp locust
#

Another idea is function types merge:

export MyType = Clients["/path"]["get"] | ({ o: string }) = infer R;

but I find it more verbose and less readable.

pliant vector
#

Have you tried a different name? The word "get" has special meaning in JS/TS.

sharp locust
#

Btw. I've got one more idea:

type API = {
  "/blog/{id}": {
    get: <T>(args: ReqType & T) => ResponseType;
    post: <T>(args: ReqType & T) => ResponseType;
  };
};

type Clients<T extends keyof API, M extends keyof API[T]> = API[T][M];

use:

type MyType = Clients<"/blog/{id}", "get"><{ o: string }>;

e.g. passing path via generic.

lean mountain
#

Generic specialization only works on values, not types.