Skip to content

Commit

Permalink
fix: predicates that return any will now behave property in TS (#5987)
Browse files Browse the repository at this point in the history
Resolves #5986
  • Loading branch information
benlesh committed Jan 26, 2021
1 parent 032f681 commit f5ae97d
Show file tree
Hide file tree
Showing 12 changed files with 51 additions and 32 deletions.
2 changes: 0 additions & 2 deletions api_guard/dist/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,6 @@ export declare type Head<X extends readonly any[]> = ((...args: X) => any) exten

export declare function identity<T>(x: T): T;

export declare function iif<T>(condition: () => true, trueResult: ObservableInput<T>, falseResult: ObservableInput<any>): Observable<T>;
export declare function iif<F>(condition: () => false, trueResult: ObservableInput<any>, falseResult: ObservableInput<F>): Observable<F>;
export declare function iif<T, F>(condition: () => boolean, trueResult: ObservableInput<T>, falseResult: ObservableInput<F>): Observable<T | F>;

export interface InteropObservable<T> {
Expand Down
7 changes: 0 additions & 7 deletions api_guard/dist/types/operators/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ export declare function first<T>(predicate: BooleanConstructor): OperatorFunctio
export declare function first<T, D>(predicate: BooleanConstructor, defaultValue: D): OperatorFunction<T, TruthyTypesOf<T> | D>;
export declare function first<T, S extends T>(predicate: (value: T, index: number, source: Observable<T>) => value is S, defaultValue?: S): OperatorFunction<T, S>;
export declare function first<T, S extends T, D>(predicate: (value: T, index: number, source: Observable<T>) => value is S, defaultValue: D): OperatorFunction<T, S | D>;
export declare function first<T, D>(predicate: (value: T, index: number, source: Observable<T>) => false, defaultValue: D): OperatorFunction<T, D>;
export declare function first<T>(predicate: (value: T, index: number, source: Observable<T>) => false): OperatorFunction<T, never>;
export declare function first<T, D = T>(predicate: (value: T, index: number, source: Observable<T>) => boolean, defaultValue?: D): OperatorFunction<T, T | D>;

export declare const flatMap: typeof mergeMap;
Expand All @@ -149,8 +147,6 @@ export declare function last<T>(predicate: BooleanConstructor): OperatorFunction
export declare function last<T, D>(predicate: BooleanConstructor, defaultValue: D): OperatorFunction<T, TruthyTypesOf<T> | D>;
export declare function last<T, D = T>(predicate?: null, defaultValue?: D): OperatorFunction<T, T | D>;
export declare function last<T, S extends T>(predicate: (value: T, index: number, source: Observable<T>) => value is S, defaultValue?: S): OperatorFunction<T, S>;
export declare function last<T, D>(predicate: (value: T, index: number, source: Observable<T>) => false, defaultValue: D): OperatorFunction<T, D>;
export declare function last<T>(predicate: (value: T, index: number, source: Observable<T>) => false): OperatorFunction<T, never>;
export declare function last<T, D = T>(predicate: (value: T, index: number, source: Observable<T>) => boolean, defaultValue?: D): OperatorFunction<T, T | D>;

export declare function map<T, R>(project: (value: T, index: number) => R, thisArg?: any): OperatorFunction<T, R>;
Expand Down Expand Up @@ -255,7 +251,6 @@ export declare function shareReplay<T>(config: ShareReplayConfig): MonoTypeOpera
export declare function shareReplay<T>(bufferSize?: number, windowTime?: number, scheduler?: SchedulerLike): MonoTypeOperatorFunction<T>;

export declare function single<T>(predicate: BooleanConstructor): OperatorFunction<T, TruthyTypesOf<T>>;
export declare function single<T>(predicate: (value: T, index: number, source: Observable<T>) => false): OperatorFunction<T, never>;
export declare function single<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean): MonoTypeOperatorFunction<T>;

export declare function skip<T>(count: number): MonoTypeOperatorFunction<T>;
Expand Down Expand Up @@ -297,8 +292,6 @@ export declare function takeLast<T>(count: number): MonoTypeOperatorFunction<T>;

export declare function takeUntil<T>(notifier: ObservableInput<any>): MonoTypeOperatorFunction<T>;

export declare function takeWhile<T>(predicate: (value: T, index: number) => false, inclusive: true): MonoTypeOperatorFunction<T>;
export declare function takeWhile<T>(predicate: (value: T, index: number) => false, inclusive?: false): OperatorFunction<T, never>;
export declare function takeWhile<T>(predicate: BooleanConstructor): OperatorFunction<T, Exclude<T, Falsy> extends never ? never : T>;
export declare function takeWhile<T>(predicate: BooleanConstructor, inclusive: false): OperatorFunction<T, Exclude<T, Falsy> extends never ? never : T>;
export declare function takeWhile<T>(predicate: BooleanConstructor, inclusive: true): MonoTypeOperatorFunction<T>;
Expand Down
17 changes: 13 additions & 4 deletions spec-dtslint/observables/iif-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@ it('should error for incorrect parameters', () => {
});

it('should infer correctly', () => {
const r0 = iif(() => false, a$, b$); // $ExpectType Observable<B>
const r1 = iif(() => true, a$, b$); // $ExpectType Observable<A>
const r0 = iif(() => false, a$, b$); // $ExpectType Observable<A | B>
const r1 = iif(() => true, a$, b$); // $ExpectType Observable<A | B>
const r2 = iif(randomBoolean, a$, b$); // $ExpectType Observable<A | B>
const r3 = iif(() => false, a$, EMPTY); // $ExpectType Observable<never>
const r4 = iif(() => true, EMPTY, b$); // $ExpectType Observable<never>
const r3 = iif(() => false, a$, EMPTY); // $ExpectType Observable<A>
const r4 = iif(() => true, EMPTY, b$); // $ExpectType Observable<B>
const r5 = iif(randomBoolean, EMPTY, EMPTY); // $ExpectType Observable<never>
});


it('should support inference from a predicate that returns any', () => {
function alwaysTrueButReturnsAny(): any {
return true;
}

const o$ = iif(alwaysTrueButReturnsAny, a$, b$) // $ExpectType Observable<A | B>
});
10 changes: 9 additions & 1 deletion spec-dtslint/operators/first-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,12 @@ it('should work properly with the Boolean constructor', () => {
const o3 = of('' as const, 'hi' as const).pipe(first(Boolean)); // $ExpectType Observable<"hi">
const o4 = of(0 as const, 'hi' as const).pipe(first(Boolean)); // $ExpectType Observable<"hi">
const o5 = of(0 as const, 'hi' as const, 'what' as const).pipe(first(Boolean)); // $ExpectType Observable<"hi" | "what">
})
});

it('should support inference from a predicate that returns any', () => {
function isTruthy(value: number): any {
return !!value;
}

const o$ = of(1).pipe(first(isTruthy)); // $ExpectType Observable<number>
});
14 changes: 11 additions & 3 deletions spec-dtslint/operators/last-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,21 @@ it('should default D to T with a predicate', () => {
});

it('should handle predicates that always return false properly', () => {
const a = of('foo', 'bar').pipe(last(() => false as const)); // $ExpectType Observable<never>
const b = of('foo', 'bar').pipe(last(() => false as const, 1337 as const)); // $ExpectType Observable<1337>
const a = of('foo', 'bar').pipe(last(() => false as const)); // $ExpectType Observable<string>
const b = of('foo', 'bar').pipe(last(() => false as const, 1337 as const)); // $ExpectType Observable<string | 1337>
});

it('should handle Boolean constructor properly', () => {
const a = of(0 as const, -0 as const, null, undefined, false as const, '' as const, 0n as const).pipe(last(Boolean)); // $ExpectType Observable<never>
const b = of(0 as const, -0 as const, null, undefined, false as const, '' as const, 0n as const).pipe(last(Boolean, 'test' as const)); // $ExpectType Observable<"test">
const c = of(0 as const, -0 as const, null, 'hi' as const, undefined, false as const, '' as const, 0n as const).pipe(last(Boolean)); // $ExpectType Observable<"hi">
const d = of(0 as const, -0 as const, null, 'hi' as const, undefined, false as const, '' as const, 0n as const).pipe(last(Boolean, 'test' as const)); // $ExpectType Observable<"test" | "hi">
})
});

it('should support inference from a predicate that returns any', () => {
function isTruthy(value: number): any {
return !!value;
}

const o$ = of(1).pipe(last(isTruthy)); // $ExpectType Observable<number>
});
10 changes: 9 additions & 1 deletion spec-dtslint/operators/single-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,13 @@ it('should handle Boolean constructor properly', () => {
});

it('should handle predicates that always return false properly', () => {
const a = of(1, 2, 3, 4).pipe(single(() => false as const)); // $ExpectType Observable<never>
const a = of(1, 2, 3, 4).pipe(single(() => false as const)); // $ExpectType Observable<number>
});

it('should support inference from a predicate that returns any', () => {
function isTruthy(value: number): any {
return !!value;
}

const o$ = of(1).pipe(single(isTruthy)); // $ExpectType Observable<number>
});
10 changes: 9 additions & 1 deletion spec-dtslint/operators/takeWhile-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ it('should properly support Boolean constructor', () => {
});

it('should properly handle predicates that always return false', () => {
const a = of(1, 2, 3).pipe(takeWhile(() => false as const)); // $ExpectType Observable<never>
const a = of(1, 2, 3).pipe(takeWhile(() => false as const)); // $ExpectType Observable<number>
const b = of(1, 2, 3).pipe(takeWhile(() => false as const, true)); // $ExpectType Observable<number>
});

it('should support inference from a predicate that returns any', () => {
function isTruthy(value: number): any {
return !!value;
}

const o$ = of(1).pipe(takeWhile(isTruthy)); // $ExpectType Observable<number>
});
6 changes: 0 additions & 6 deletions src/internal/observable/iif.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ import { Observable } from '../Observable';
import { defer } from './defer';
import { ObservableInput } from '../types';

export function iif<T>(condition: () => true, trueResult: ObservableInput<T>, falseResult: ObservableInput<any>): Observable<T>;

export function iif<F>(condition: () => false, trueResult: ObservableInput<any>, falseResult: ObservableInput<F>): Observable<F>;

export function iif<T, F>(condition: () => boolean, trueResult: ObservableInput<T>, falseResult: ObservableInput<F>): Observable<T | F>;

/**
* Checks a boolean at subscription time, and chooses between one of two observable sources
*
Expand Down
2 changes: 0 additions & 2 deletions src/internal/operators/first.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ export function first<T, S extends T, D>(
predicate: (value: T, index: number, source: Observable<T>) => value is S,
defaultValue: D
): OperatorFunction<T, S | D>;
export function first<T, D>(predicate: (value: T, index: number, source: Observable<T>) => false, defaultValue: D): OperatorFunction<T, D>;
export function first<T>(predicate: (value: T, index: number, source: Observable<T>) => false): OperatorFunction<T, never>;
export function first<T, D = T>(
predicate: (value: T, index: number, source: Observable<T>) => boolean,
defaultValue?: D
Expand Down
2 changes: 0 additions & 2 deletions src/internal/operators/last.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ export function last<T, S extends T>(
predicate: (value: T, index: number, source: Observable<T>) => value is S,
defaultValue?: S
): OperatorFunction<T, S>;
export function last<T, D>(predicate: (value: T, index: number, source: Observable<T>) => false, defaultValue: D): OperatorFunction<T, D>;
export function last<T>(predicate: (value: T, index: number, source: Observable<T>) => false): OperatorFunction<T, never>;
export function last<T, D = T>(
predicate: (value: T, index: number, source: Observable<T>) => boolean,
defaultValue?: D
Expand Down
1 change: 0 additions & 1 deletion src/internal/operators/single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { operate } from '../util/lift';
import { OperatorSubscriber } from './OperatorSubscriber';

export function single<T>(predicate: BooleanConstructor): OperatorFunction<T, TruthyTypesOf<T>>;
export function single<T>(predicate: (value: T, index: number, source: Observable<T>) => false): OperatorFunction<T, never>;
export function single<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean): MonoTypeOperatorFunction<T>;

/**
Expand Down
2 changes: 0 additions & 2 deletions src/internal/operators/takeWhile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { OperatorFunction, MonoTypeOperatorFunction, Falsy } from '../types';
import { operate } from '../util/lift';
import { OperatorSubscriber } from './OperatorSubscriber';

export function takeWhile<T>(predicate: (value: T, index: number) => false, inclusive: true): MonoTypeOperatorFunction<T>;
export function takeWhile<T>(predicate: (value: T, index: number) => false, inclusive?: false): OperatorFunction<T, never>;
export function takeWhile<T>(predicate: BooleanConstructor): OperatorFunction<T, Exclude<T, Falsy> extends never ? never : T>;
export function takeWhile<T>(
predicate: BooleanConstructor,
Expand Down

0 comments on commit f5ae97d

Please sign in to comment.