Использование infer для захвата в штучной упаковке наблюдаемого значения - PullRequest
1 голос
/ 19 октября 2019

Я создал pipe для преобразования любого источника в Observable следующим образом:

// If T is like an Observable it infers the inner value, otherwise it returns T
type Unobservable<T> = T extends Observable<infer R> ? R : T;

@Pipe({
  name: 'toObservable'
})
export class ToObservablePipe implements PipeTransform {
  transform<T>(value: T): Observable<Unobservable<T>> {
    return isObservable(value) ? value : just(value);
  }
}

Сама функциональность работает, однако я столкнулся с проблемой с типом возвращаемого значения. В настоящее время он показывает следующую ошибку:

Type '(T & Observable) |Observable »нельзя назначить типу« Observable> ». Тип 'T & Observable' нельзя назначить типу 'Observable>'. Тип «unknown» не может быть назначен типу «Unobservable».

Просто для контекста, использование канала может быть примерно таким:

<code><h4>Array source</h4>

<pre>{{ array | toObservable | async | json }}

Как я могу сделать компилятор "счастливым"? :)

DEMO

Ответы [ 4 ]

0 голосов
/ 19 октября 2019

Вместо этого можно использовать перегрузку, например @ angular / AsyncPipe :

import { Pipe, PipeTransform } from "@angular/core";
import { isObservable, Observable, of as just } from "rxjs";

@Pipe({
  name: "toObservable"
})
export class ToObservablePipe implements PipeTransform {
  transform<T>(value: null): null;
  transform<T>(value: undefined): undefined;
  transform<T>(value: Observable<T> | null | undefined): Observable<T> | null | undefined;
  transform<T>(value: T): Observable<T> | null | undefined;
  transform(value: Observable<any> | null | undefined): any {
    // can be also if (value === null || value === undefined) ...
    if (value == null) return value;

    return isObservable(value) ? value : just(value);
  }
}

WORKING DEMO


Некоторые тестовые случаи :

const obj = { label: 1 };

// Error!
new ToObservablePipe().transform(null).subscribe(response => console.log(response));

// Error!
new ToObservablePipe().transform(undefined).subscribe(response => console.log(response));

// Inferred as string
new ToObservablePipe().transform('').subscribe(response => console.log(response));

// Inferred as number
new ToObservablePipe().transform(0).subscribe(response => console.log(response));

// Inferred as { label: number }
new ToObservablePipe().transform(just(obj)).subscribe(response => console.log(response));

// Inferred as { label: number }[]
new ToObservablePipe().transform([obj]).subscribe(response => console.log(response));

// Inferred as { label: number }
new ToObservablePipe().transform(obj).subscribe(response => console.log(response));
0 голосов
/ 19 октября 2019
type Unobservable<T> = T extends Observable<infer R> ? R : T;

означает, что Unobservable может быть передано <T> типа, или any, поскольку R может быть неизвестного типа.

А здесь transform<T>(value: T): Observable<Unobservable<T>> Вы ожидаете возврата кбыть того же типа, что и преобразование, и это не всегда так, поскольку мы объявляем, что это может быть any тип, и просим вывести его.

Чтобы сделать то, что у вас есть, введите transform response для any.

Observable<Unobservable<any>>

В конце концов, ненаблюдаемый - это просто любой тип, я не понимаю, как его использовать.

0 голосов
/ 19 октября 2019
import { Pipe, PipeTransform } from "@angular/core";
import { isObservable, Observable, of as just } from "rxjs";

type Unobservable<T> = T extends Observable<infer R> ? R : T;

@Pipe({
  name: "toObservable"
})
export class ToObservablePipe implements PipeTransform {
  transform<T>(value: T): Observable<Unobservable<T>> {
    return isObservable(value) ? value as Observable<Unobservable<T>> : 
just(value) as Observable<Unobservable<T>>;
  }
}

Кастинг удовлетворит компилятор, если это все, что вы хотите.

0 голосов
/ 19 октября 2019

вы можете достичь того же результата, используя этот метод, не используя infer.

import { Pipe, PipeTransform } from "@angular/core";
import { isObservable, Observable, of as just } from "rxjs";

@Pipe({
  name: "toObservable"
})
export class ToObservablePipe implements PipeTransform {
  transform<T>(value: T): T | Observable<T> {
    return isObservable(value) ? value : just(value);
  }
}

, но если вы хотите использовать infer, я могу предложить вам это решение

import { Pipe, PipeTransform } from "@angular/core";
import { isObservable, Observable, of as just } from "rxjs";

type Unobservable<T> = T extends Observable<infer R> ? R : T;
type ObservableOrAny<T> = Observable<T> | T;

@Pipe({
  name: "toObservable"
})
export class ToObservablePipe implements PipeTransform {
  transform<T>(value: ObservableOrAny<T>): Observable<Unobservable<ObservableOrAny<T>>> {
    return isObservable(value) ? value : just(value);
  }
}
...