Skip to content

Commit 4df531f

Browse files
test(typings): Add compile tests for filter, find, first, last
These are from #2119
1 parent 128fb9c commit 4df531f

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

spec/operators/filter-spec.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,4 +272,22 @@ describe('Observable.prototype.filter', () => {
272272
expectObservable(r, unsub).toBe(expected);
273273
expectSubscriptions(source.subscriptions).toBe(subs);
274274
});
275-
});
275+
276+
it('should not be compile error', () => {
277+
// tslint:disable no-unused-variable
278+
279+
{
280+
// x is `Observable<string | number>`
281+
const x: Rx.Observable<string | number> = Observable.from([1, 'aaa', 3, 'bb']);
282+
// This type guard will narrow a `string | number` to a string in the examples below
283+
const isString = (x: string | number): x is string => typeof x === 'string';
284+
285+
// Here, `s` is a string in the second filter predicate after the type guard (yay - intellisense!)
286+
const guardedFilter = x.filter(isString).filter(s => s.length === 2); // Observable<string>
287+
// In contrast, this type of regular boolean predicate still maintains the original type
288+
const boolFilter = x.filter(s => typeof s === 'number'); // Observable<string | number>
289+
}
290+
291+
// tslint:disable enable
292+
});
293+
});

spec/operators/find-spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,22 @@ describe('Observable.prototype.find', () => {
155155
expectObservable((<any>source).find(predicate)).toBe(expected);
156156
expectSubscriptions(source.subscriptions).toBe(subs);
157157
});
158+
159+
it('should not be compile error', () => {
160+
// tslint:disable no-unused-variable
161+
162+
{
163+
// x is `Observable<string | number>`
164+
const x: Rx.Observable<string | number> = Observable.from([1, 'aaa', 3, 'bb']);
165+
// This type guard will narrow a `string | number` to a string in the examples below
166+
const isString = (x: string | number): x is string => typeof x === 'string';
167+
168+
// After the type guard `find` predicate, the type is narrowed to string
169+
const guardedFind = x.find(isString).filter(s => s.length > 1).map(s => s.substr(1)); // Observable<string>
170+
// In contrast, a boolean predicate maintains the original type
171+
const boolFind = x.find(x => typeof x === 'string'); // Observable<string | number>
172+
}
173+
174+
// tslint:disable enable
175+
});
158176
});

spec/operators/first-spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,25 @@ describe('Observable.prototype.first', () => {
214214
expectObservable(e1.first(predicate, resultSelector)).toBe(expected);
215215
expectSubscriptions(e1.subscriptions).toBe(sub);
216216
});
217+
218+
it('should not be compile error', () => {
219+
// tslint:disable no-unused-variable
220+
221+
{
222+
// x is `Observable<string | number>`
223+
const x: Rx.Observable<string | number> = Observable.from([1, 'aaa', 3, 'bb']);
224+
// This type guard will narrow a `string | number` to a string in the examples below
225+
const isString = (x: string | number): x is string => typeof x === 'string';
226+
227+
// After the type guard `first` predicates, the type is narrowed to string
228+
const guardedFirst1 = x.first(isString).filter(s => s.length > 1).map(s => s.substr(1)); // Observable<string>
229+
const guardedFirst2 = x.first(isString, s => s.substr(0)).filter(s => s.length > 1); // Observable<string>
230+
// Without a resultSelector, `first` maintains the original type (TS can't do this yet)
231+
const boolFirst1 = x.first(x => typeof x === 'string', null, ''); // Observable<string | number>
232+
// `first` still uses the `resultSelector` return type, if it exists.
233+
const boolFirst2 = x.first(x => typeof x === 'string', s => ({str: `${s}`}), {str: ''}); // Observable<{str: string}>
234+
}
235+
236+
// tslint:disable enable
237+
});
217238
});

spec/operators/last-spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,25 @@ describe('Observable.prototype.last', () => {
142142
expectObservable(e1.last(predicate, resultSelector)).toBe(expected);
143143
expectSubscriptions(e1.subscriptions).toBe(e1subs);
144144
});
145+
146+
it('should not be compile error', () => {
147+
// tslint:disable no-unused-variable
148+
149+
{
150+
// x is `Observable<string | number>`
151+
const x: Rx.Observable<string | number> = Rx.Observable.from([1, 'aaa', 3, 'bb']);
152+
// This type guard will narrow a `string | number` to a string in the examples below
153+
const isString = (x: string | number): x is string => typeof x === 'string';
154+
155+
// After the type guard `last` predicates, the type is narrowed to string
156+
const guardedLast1 = x.last(isString).filter(s => s.length > 1).map(s => s.substr(1)); // Observable<string>
157+
const guardedLast2 = x.last(isString, s => s.substr(0)).filter(s => s.length > 1); // Observable<string>
158+
// Without a resultSelector, `last` maintains the original type (TS can't do this yet)
159+
const boolLast1 = x.last(x => typeof x === 'string', null, ''); // Observable<string | number>
160+
// `last` still uses the `resultSelector` return type, if it exists.
161+
const boolLast2 = x.last(x => typeof x === 'string', s => ({str: `${s}`}), {str: ''}); // Observable<{str: string}>
162+
}
163+
164+
// tslint:disable enable
165+
});
145166
});

0 commit comments

Comments
 (0)