Repository navigation
Add named type arguments #38913
Description
Search Terms
generics, type parameters, named parameter, named type parameter, type argument, named type argument
Suggestion
It should be possible to pass type arguments to a generic by name rather than positionally, eg.
interface Foo<T = SomeDefaultType, U> { ... }
// Current syntax
const foo: Foo<SomeDefaultType, string> ...
// Proposed syntax
const foo: Foo<U = string> ...
// yields foo: Foo<SomeDefaultValue, string>This is loosely inspired on python's named arguments:
def foo(bar = "I'm bar", baz):
...
foo(baz="I'm baz")Use Cases
Generics only accept positional type arguments. If you have a generic accepting many type arguments, most or all of which having default values such as:
interface Handler<Type = string, TPayload = object, TOutput = void> {
type: Type
handle(payload: TPayload): TOutput
}Let's say we have a class which implements the Handler interface but the defaults for Type and TPayload are fine for us and we only want to specify a type for TOuput, currently it is mandatory that we pass type arguments for Type and TPayload:
class Foo implements Handler<string, object, Promise<number>>If it was possible to pass type arguments by name we could use the considerably terser form:
class Foo implements Handler<TOutput=Promise<number>>Examples
Fastify exposes types generic over many parameters with default values, such as
interface FastifyRequest<
HttpRequest = http.IncomingMessage,
Query = DefaultQuery,
Params = DefaultParams,
Headers = DefaultHeaders,
Body = DefaultBody
> { ...With the proposed syntax we could create specialized interfaces with much less overhead such as
import * as fastify from 'fastify';
const app = fastify();
app.get('/users/:id', async (req: FastifyRequest<Params = {id: string }>, res: FastifyReply) => {
// req.params is strongly typed now
})Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Activity
[-]Allow named type parameters on generics[/-][+]Add named type arguments[/+]RyanCavanaugh commentedon Jun 3, 2020
The arguments are the thing that go in at the use site; the parameters are the ones at the declaration site.
pedrolcn commentedon Jun 3, 2020
Thanks for the clarification, I always mix these terms up. I updated the description to use them correctly
Airblader commentedon Feb 19, 2021
This is a duplicate of #23696 and has been superseded by #26349 since it doesn't suffer from issues such as this one.
ivan-kleshnin commentedon Feb 24, 2021
Ingo Bürk (@Airblader) how a weaker proposal can supersede a stronger one?
(optional sequential type arguments) vs (named type arguments). I don't buy that issues in #26346 are inherent to the idea itself.
Airblader commentedon Feb 24, 2021
You can call it an alternate approach to the raised use-cases instead of a superseding approach, if you want, but that's what Wesley Wigham (@weswigham) called it when he closed the issue. Either way, this is a duplicate.
The use-case raised in the issue here can just as well be solved with the superseding/alternate approach, so it's a valid replacement. In contrast, it doesn't raise issues such as type argument naming becoming public API.
paulnelson2 commentedon Feb 16, 2022
Ingo Bürk (@Airblader) The OP's goal of making the declaration terser would indeed be covered by pr #26349, but there's another use imo, which is clarity to the reader, in the same way that named arguments to functions confer.
I'd rather say this:
(1)
than this:
(2)
In 2, does the reader know what string is for in B? Or number or Date?
(contrived example)
owenallenaz commentedon Jul 1, 2022
paulnelson2 In addition to the example presented there, there is also the case where the user wants to utilize one of the arguments, but not all. For example in
expressthere is following interface:It is quite logical to want to overwrite just one of those params, such as the
ReqBody, in which I would like to be able to do:Zamiell commentedon Jul 10, 2022
In the OP, pedrolcn proposes that named type arguments would allow end-users to more-easily instantiate generic objects with lots of default parameters.
However, I believe that named type arguments have an entirely different utility - for documentation. Consider the following.
Before TypeScript 4.0, the following code was common:
This is not ideal, because we are forced to write comments to explain what the code does. TypeScript 4.0 fixes this problem by allowing us to write more expressive code with the named tuple feature:
Much better! No comments necessary.
Next, let's consider the case of a Map:
The similarity to the previous example should be clear. It would be ideal to refactor away this JSDoc comment in the exact same way that we just did with the tuple. But how? There's no analogous "named tuple" feature for generic type parameters.
What we really want to do is to write this, using the same colon-syntax as with named tuples:
Or, using the equals-syntax that pedrolcn proposes in the OP:
Either way, the idea is that the aliases should show up whenever someone mouses over the map in VSCode, making the code self-documenting.