I was checking RxJS api documents. I always face problem in understanding the weird syntax written on the top, Which is like below as an example:
combineLatest<O extends ObservableInput<any>, R>(...observables: (O | ((...values: ObservedValueOf<O>[]) => R) | SchedulerLike)[]): Observable<R>
I wanted to understand this kind of syntax and further write it by my own.
So, It will be great help if anyone explain what's going on in above example.
function combineLatest<O extends ObservableInput<any>, R>(...observables: (O | ((...values: ObservedValueOf<O>[]) => R) | SchedulerLike)[]): Observable<R>
Let's break it down.
Okay, that's a function
function combineLatest(args): Result
which takes some parameters and returns a result of type Result
But it's also a special kind of functions called generic functions. The purpose of generic is to provide meaningful type constraints between members.
function combineLatest<T>(args): Result
/\
now it's a generic function
The T
here is a type variable which allows us to capture the type the user provides, so that we can use that information later. For example, we can use T
as the return type:
function combineLatest<T>(args): T
so that the following call:
combineLatest<string>(someParams) // returns `string`
will give us the result of type string
since we explicitly set T
to be string
.
We can also use that T
type variable for any of arguments of the function.
function combineLatest<T>(arg: T): T
And now we can use type argument inference:
combineLatest('someStr') // returns `string`
That is, we tells the compiler to set the value of T
for us automatically based on the type of the argument we pass in. We've passed a string
so that we've got a string
.
We can use as many type variables as we want. And also we can call them whatever we want.
function combineLatest<Type1, Type2, ...>(...)
Generics are great and sometimes we want to make some actions with the parameters we've passed in:
function combineLatest<T>(arg: T): T {
arg.name = arg.name.toLowerCase(); // Property 'name' does not exist on type 'T'
return arg;
}
As you can see the compiler can't understand what type of arg
. To remedy this we can denote the constraint for our T
type variable with the help of extends
operator:
interface ItemWithName {
name: string;
}
function combineLatest<T extends ItemWithName>(arg: T): T {
arg.name = arg.name.toLowerCase();
return arg;
}
Now the compiler knows that arg has the name
property of string
.
Now let's come back to our initial declaration:
combineLatest<O extends ObservableInput<any>, R>(...): Observable<R>
We can see here that this function uses two type variables:
O
type variable which stands for Observable. It is constrained to be type of ObservableInput<any>
R
stands for ResultThe result of the combineLatest
function should be Observable
of type R
. For example we can force that type by calling this function like:
combineLatest<any, MyClass>(...) // returns Observable<MyClass>
Now let's look at the parameters this function can take:
...observables: (O | ((...values: ObservedValueOf<O>[]) => R) | SchedulerLike)[]
We can see the rest parameters operator here. Let's simpify the expression above so we imagine something like:
...observables: CombinedType[]
This means that the combineLatest
function can take zero and more arguments which will be later gathered together into one variable observables
.
combineLatest(); // without parameters
combineLatest(obs1); // one parameter
combineLatest(obs1, obs2, ..etc) // many parameters
So what's the type of this parameter
CombinedType
O | ((...values: ObservedValueOf<O>[]) => R) | SchedulerLike
It can be:
the type of O
type variable which is constrained to be type of ObservableInput<any>
we discussed earlier
or it can be function (...values: ObservedValueOf<O>[]) => R
which takes zero or more parameters of type ObservedValueOf<O>
and returns R
type variable type. Note that this returned type can be used to infer the return type of the combineLatest
function.
or it can be type of SchedulerLike
interface.
I think there shouldn't be any problem with understanding TypeScript type declarations if you split them into small pieces.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With