3

I have the following scenario:

interface Foo<GenericA, GenericB> {}

I want to declare a variable of type Foo, but only specify the second generic, e.g.:

const foo: Foo<GenericBType>;

I can only do that if I write

const foo: Foo<unknown, GenericBType>;

It is not really that bad, but it doesn't feel right either, as I just want to make use of the second type, and I am not interested in the first at all.

Is there any other way I can write this in a more succinct way?

PS: This is about the expressjs types. I want to type the Request object, but I only want to consider the type of the Query params, which is the fourth generic in the type of the Request:

 interface Request<
    P = core.ParamsDictionary,
    ResBody = any,
    ReqBody = any,
    ReqQuery = core.Query,
    Locals extends Record<string, any> = Record<string, any>
> extends core.Request<P, ResBody, ReqBody, ReqQuery, Locals> {}

so I would have to write something like:

req: Request<unknown, unknown, unknown, Foo>

and it's really not beautiful...

Alexandru Pirvu
  • 505
  • 1
  • 4
  • 12
  • 2
    You can create an alias for `Foo`. See [this example](https://tsplay.dev/NrGVVm) – captain-yossarian from Ukraine Dec 16 '21 at 08:21
  • @captain-yossarian Thanks for the suggestion, but this would still mean that I have to replace a type with unknown – Alexandru Pirvu Dec 16 '21 at 08:33
  • How can it be valid to create a *variable* Foo specifying B but not A (not even unknown or any)? That doesn't make sense. Variables have a specific type. Otherwise you could say `let x = Foo` or `let x = Foo<>`, right? What would that even mean? If it's succinctness you're after, @captain-yossarian has already given you a solution. Me thinks you don't understand what you are asking. What specifically/semantically are you trying to achieve with `Request`? – Inigo Dec 16 '21 at 10:35
  • @captain-yossarian But how do I specify only one of the types? If you look at how express defined their request, the generics are optional, but as any optionals in typescript, you have to define everything up until the position you are really interested in. So, in the case of Request, if I want to just define the ReqQuery, I also have to specify P, ResBody and ReqBody (but not Locals). – Alexandru Pirvu Dec 16 '21 at 12:11
  • @AlexandruPirvu if first argument is required, it means that you should provide it. You can't just skip first argument. Imagine situation when you have a function with one required argument and you want to expect function do the job without providing this argument. – captain-yossarian from Ukraine Dec 16 '21 at 12:15
  • @captain-yossarian You are right, but in my case, all the params are optional. So you can argue that it's the same as with optional params in functions. You cannot pas the 3rd optional param without passing the first two even if those are also optional. What I would like actually is a way to specify types based on names, e.g. like you specify params based on names in python. – Alexandru Pirvu Dec 16 '21 at 13:27
  • 1
    If all generic are optional then if you provide only one generic argument TS compiler will treat it as a first generic. There is no wildcard like `_` or `*`. I think using `unknown` is the best way to handle missing generics – captain-yossarian from Ukraine Dec 16 '21 at 13:29
  • 1
    @captain-yossarian Can you put this as an answer so I can accept it? – Alexandru Pirvu Dec 17 '21 at 09:15

1 Answers1

0

The simplest solution here is to create type alias:

interface Foo<GenericA, GenericB> { }

type Foo2<B> = Foo<unknown, B>

declare const foo: Foo2<'generic B'>

If all generics are optional then if you provide only one generic argument TS compiler will treat it as a first generic. There is no wildcard like _ or *. I think using unknown is the best way to handle missing generics.

You might looking for existential types (issues/10571). This is not supported by typescript. Existential types feature was supported in Flow but as you might have noticed it is deprecated now due to unsafety:

Triggers when you use the * (existential) type, as this type is unsafe and usually just equivalent to any. The effect of * can generally be achieved by simply not providing a type annotation.