[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.

时间: 2024-12-26 08:48:20

[TypeScript] Understanding Generics with RxJS的相关文章

[TypeScript] Understanding Decorators

Decorators are a feature of TypeScript that are becoming more and more common in many major libraries. This lesson walks you through what decorators are and how to create your own. Nomarl way to decorate a object : const Person = {name: 'John'}; func

[RxJS] Creating Observable From Scratch

Get a better understanding of the RxJS Observable by implementing one that's similar from the ground up. class SafeObserver { constructor(destination) { this.destination = destination; } next(value) { const destination = this.destination; if (destina

[TypeScript] The Basics of Generics in TypeScript

It can be painful to write the same function repeatedly with different types. Typescript generics allow us to write 1 function and maintain whatever type(s) our function is given. This lesson covers syntax and a basic use case for Typescript generics

[Typescript] Introduction to Generics in Typescript

If Typescript is the first language in which you've encountered generics, the concept can be quite difficult to understand. We skip the lecture in this lesson and dive straight into a real-world use-case that is guaranteed to help you understand the

[RxJS] Add debug method to Observable in TypeScript

Observable.prototype.debug = function(message: any) { return this.do( (next) => { if(!environment.production) { console.log(message, next); } }, (err) => { if(!environment.production) { console.error(message, err) } }, () => { if(!environment.pro

[TypeScript] Generic Functions, class, Type Inference and Generics

Generic Fucntion: For example we have a set of data and an function: interface HasName { name: string; } const heros: HasName[] = [ {name: 'Jno'}, {name: 'Miw'}, {name: 'Ggr'}, {name: 'Gew'}, {name: 'Wfe'} ]; function cloneArray(ary: any[]): any[] {

rxjs 入门--环境配置

原文: https://codingthesmartway.com/getting-started-with-rxjs-part-1-setting-up-the-development-environment-creating-observables/ ---------------------------------------------------------------- Getting Started With RxJS – Part 1: Setting Up The Develo

TypeScript手册翻译系列8-常见错误与Mixins

常见错误 下面列出了在使用TypeScript语言和编译器期间,经常会遇到的一些常见错误. "tsc.exe" exited with error code 1. 解决方案: 检查文件编码为UTF-8 - https://typescript.codeplex.com/workitem/1587 external module XYZ cannot be resolved 解决方案:检查模块路径是大小写敏感- https://typescript.codeplex.com/workit

TypeScript手册翻译系列7-泛型

泛型(Generics) 软件工程的一个主要部分是建立不仅有良好定义和一致性APIs,而且是可重用的组件(components).使用今天数据以及明日数据的组件才能够最为灵活地构建大规模软件系统. 在类似C#和Java等语言中,工具箱中创建可重用组件的一个主要工具就是泛型(generics),即能够创建可以使用各种类型而不是单一类型的组件.这使得用户可以用自己的类型来调用这些组件. Hello World of Generics 我们先来做一个泛型的"hello world":iden