[TypeScript] Understanding Generics with RxJS

Posted Answer1215

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[TypeScript] Understanding Generics with RxJS相关的知识,希望对你有一定的参考价值。

Libraries such as RxJS use generics heavily in their definition files to describe how types flow through different interfaces and function calls. We can provide our own type information when we create Observables to enable all of the auto-complete & type-safety features that you would expect from Typescript. This can be achieved with minimal annotations thanks to the power of generics.

 

import Rx = require(‘rx‘);

Rx.Observable.interval(100)
    .subscribe( (x) => {
        console.log(x);
    });

The way Typescript can understand the ‘x‘ inside console.log() is type of number is because its type defination.

 

rx.all.d.ts:

    export interface ObservableStatic {
        /**
         *  Returns an observable sequence that produces a value after each period.
         *
         * @example
         *  1 - res = Rx.Observable.interval(1000);
         *  2 - res = Rx.Observable.interval(1000, Rx.Scheduler.timeout);
         *
         * @param {Number} period Period for producing the values in the resulting sequence (specified as an integer denoting milliseconds).
         * @param {Scheduler} [scheduler] Scheduler to run the timer on. If not specified, Rx.Scheduler.timeout is used.
         * @returns {Observable} An observable sequence that produces a value after each period.
         */
        interval(period: number, scheduler?: IScheduler): Observable<number>;
    }

As we can see, the return type is number.

 

So interval return a number, but how in subscribe function also know ‘x‘ is number type.

To understand that, we can go subscribe defination:

    export interface Observable<T> {
        /**
        *  Subscribes an o to the observable sequence.
        *  @param {Mixed} [oOrOnNext] The object that is to receive notifications or an action to invoke for each element in the observable sequence.
        *  @param {Function} [onError] Action to invoke upon exceptional termination of the observable sequence.
        *  @param {Function} [onCompleted] Action to invoke upon graceful termination of the observable sequence.
        *  @returns {Diposable} A disposable handling the subscriptions and unsubscriptions.
        */
        subscribe(observer: IObserver<T>): IDisposable;
        /**
        *  Subscribes an o to the observable sequence.
        *  @param {Mixed} [oOrOnNext] The object that is to receive notifications or an action to invoke for each element in the observable sequence.
        *  @param {Function} [onError] Action to invoke upon exceptional termination of the observable sequence.
        *  @param {Function} [onCompleted] Action to invoke upon graceful termination of the observable sequence.
        *  @returns {Diposable} A disposable handling the subscriptions and unsubscriptions.
        */
        subscribe(onNext?: (value: T) => void, onError?: (exception: any) => void, onCompleted?: () => void): IDisposable;

        /**
        * Subscribes to the next value in the sequence with an optional "this" argument.
        * @param {Function} onNext The function to invoke on each element in the observable sequence.
        * @param {Any} [thisArg] Object to use as this when executing callback.
        * @returns {Disposable} A disposable handling the subscriptions and unsubscriptions.
        */
        subscribeOnNext(onNext: (value: T) => void, thisArg?: any): IDisposable;
        /**
        * Subscribes to an exceptional condition in the sequence with an optional "this" argument.
        * @param {Function} onError The function to invoke upon exceptional termination of the observable sequence.
        * @param {Any} [thisArg] Object to use as this when executing callback.
        * @returns {Disposable} A disposable handling the subscriptions and unsubscriptions.
        */
        subscribeOnError(onError: (exception: any) => void, thisArg?: any): IDisposable;
        /**
        * Subscribes to the next value in the sequence with an optional "this" argument.
        * @param {Function} onCompleted The function to invoke upon graceful termination of the observable sequence.
        * @param {Any} [thisArg] Object to use as this when executing callback.
        * @returns {Disposable} A disposable handling the subscriptions and unsubscriptions.
        */
        subscribeOnCompleted(onCompleted: () => void, thisArg?: any): IDisposable;

        /**
        *  Subscribes an o to the observable sequence.
        *  @param {Mixed} [oOrOnNext] The object that is to receive notifications or an action to invoke for each element in the observable sequence.
        *  @param {Function} [onError] Action to invoke upon exceptional termination of the observable sequence.
        *  @param {Function} [onCompleted] Action to invoke upon graceful termination of the observable sequence.
        *  @returns {Diposable} A disposable handling the subscriptions and unsubscriptions.
        */
        forEach(observer: IObserver<T>): IDisposable;

        /**
        *  Subscribes an o to the observable sequence.
        *  @param {Mixed} [oOrOnNext] The object that is to receive notifications or an action to invoke for each element in the observable sequence.
        *  @param {Function} [onError] Action to invoke upon exceptional termination of the observable sequence.
        *  @param {Function} [onCompleted] Action to invoke upon graceful termination of the observable sequence.
        *  @returns {Diposable} A disposable handling the subscriptions and unsubscriptions.
        */
        forEach(onNext?: (value: T) => void, onError?: (exception: any) => void, onCompleted?: () => void): IDisposable;
    }

As we can see ‘onNext‘ method it use ‘<T>‘ it get from the ‘Observable<T>‘ interface. How‘s how TypeScript understand console.log(x)‘s x should be number type.

 

So how can we create a type safe Observable by ourselves?

Rx.Observable.create( (observer) => {
    observer.onNext({
        path: ‘/user/share‘,
        event: ‘add‘
    })
}).subscribe( (x) => {
    console.log(x);
});

For this part of code, we want Typescipt help us autocomplete and report error if we pass something like ‘x.path1‘ which not exists. 

 

First we add an interface:

interface FileEvent{
    path: string,
    event: ‘add‘ | ‘change‘
}

 

We can add the interface to the subscribe, it will give the type checking and autocomplete:

Rx.Observable.create( (observer) => {
    observer.onNext({
        path: ‘/user/share‘,
        event: ‘add‘
    })
}).subscribe( (x: FileEvent) => {
    console.log(x);
});

The problem for this is if you have multi subscriber, you don‘t want to copy this everywhere.

 

What actually we should do is provide the interface at the topest level:

Rx.Observable.create<FileEvent>( (observer) => {
    observer.onNext({
        path: ‘/user/share‘,
        event: ‘add‘
    })
}).subscribe( (x) => {
    console.log(x);
});

 

To recap, create is a generic function which means we can pass a type when we call it. Then, thanks to the type definitions that are provided by the RxJS library, this type can now flow into the observer as you can see here, it enables type safety on the onNext method, and even makes it all the way through to the subscribe method.

以上是关于[TypeScript] Understanding Generics with RxJS的主要内容,如果未能解决你的问题,请参考以下文章

typescript TypeScript Snippets #typescript

TypeScript入门五:TypeScript的接口

TypeScript系列教程--初探TypeScript

TypeScript入门三:TypeScript函数类型

typescript使用 TypeScript 开发 Vue 组件

认识 TypeScript