At the top are the arguments that have been assigned meaningful labels. Below are arguments with useless names.
Why was I waiting for this? I will explain this using the example of developing a function.
Generalized interface that supports flexible argument handling
Here's a simplified example. It is based on using the interface
IQuery
. The interface is intended to describe the characteristics of functions that perform requests for getting something. It always returns a promise and accepts a generic describing what a promise ( TReturn
) returns . This interface is quite flexible, you can use it when creating functions that take no arguments, or take an unknown number of arguments ( UParams extends any[] = []
).
interface IQuery<TReturn, UParams extends any[] = []> {
(...args: UParams): Promise<TReturn>
}
Function under test: findSongAlbum ()
We, using this interface, will write a function that searches for music albums by song title (
title
) and artist ( artist
). It returns a promise that returns a single object of type Album
:
type Album = {
title: string
}
Without TypeScript, the code for such a function would look like this:
const findSongAlbum = (title, artist) => {
// ...
const albumName = '1989';
return Promise.resolve({
title: albumName
});
}
If you write such a function in TypeScript and use the interface
IQuery
, then the type must be passed to it as the first generic parameter Album
. This ensures that the shape of what the promise returns will always match the type Album
.
const findSongAlbum: IQuery<Album> = (title, artist) => {
// ...
const albumName = '1989';
return Promise.resolve({
title: albumName
});
}
Code written before TypeScript 4.0
When developing our function, we also need to declare the parameters and indicate what types they have. In this case,
title
and artist
are strings. Let's declare a new type,, Params
and pass it as the second type for IQuery
.
In the example, which is written without using the new features of TypeScript 4.0, it
Params
will be represented by a list of types. Each element of this list defines the type of the argument. This is done in the same order in which the arguments appear when the function is declared. This is an example of using tuples .
type Params: [string, string]
const findSongAlbum: IQuery<Album, Params> = (title, artist) => {
// ...
const albumName = '1989';
return Promise.resolve({
title: albumName
});
}
By analyzing the type
Params
, you can find out that its first element string
,, assigns the type to the string
first argument, that is, - title
. The second argument, which also looks like string
, naturally, following the same line of reasoning, assigns a type to the string
second argument - artist
. This will make the argument list type safe.
Let's try to work with this function.
The hints to findSongAlbum () display meaningless argument labels
. Unfortunately, this approach to using tuples does not give us useful and reliable code argument names (labels) when working with a function. Instead, we are told that the arguments to the function are
args_0: string, args_1: string
... From this post, I can only learn, for example, that the first argument must be of type string
. The label arg_0
does not tell me that the first parameter of the function should be the name ( title
) of the musical composition I am looking for.
Code that leverages TypeScript 4.0 features
Now, with the release of the TypeScript 4.0 Release Candidate, we have labeled tuples at our disposal. We can use them to describe not only the type, but also the semantic content of the function parameter lists.
Now each element of the type
Params
will be provided with a label that will be displayed in the IDE when using the function findSongAlbum
:
type Params: [title: string, artist: string]
const findSongAlbum: IQuery<Album, Params> = (title, artist) => {
// ...
const albumName = '1989';
return Promise.resolve({
title: albumName
});
}
And here is how working with this function looks like
The hints for findSongAlbum () display argument labels to help us understand the meaning of these arguments, which is quite useful.
As you can see, now, instead of a view hint,
arg_0: string
we are given a hint title: string
. This means that we now know not only that the function needs to be passed a string, but also what exactly this string should contain.
What are you especially missing in TypeScript?