Closed
Description
TypeScript Version: 3.6.0-dev.20190720
Search Terms: type narrowing undefined generics early return
Code
type BaseEvents = {[event: string]: Array<any>};
type Cbs<A extends BaseEvents> = {
[P in keyof A]: Array<(...args: A[P]) => boolean | Promise<boolean>> | undefined;
}
export class Events<T extends BaseEvents> {
private cbs: Cbs<T> = {} as any;
async emit<P extends keyof T>(event: P, ...args: T[P]) {
const arr = this.cbs[event]; // typeof arr = ((...args: T[P]) => boolean | Promise<boolean>)[] | undefined
type NonNullArr = NonNullable<typeof arr>; // ((...args: T[P]) => boolean | Promise<boolean>)[]
if (typeof arr === 'undefined') {
return;
}
// typeof arr should now be ((...args: T[P]) => boolean | Promise<boolean>)[]
const nonNullArr: NonNullArr = arr; // TS2322: Type '((...args: T[P]) => boolean | Promise<boolean>)[] | undefined' is not assignable to type 'NonNullable<Cbs<T>[P]>'.
// Type 'undefined' is not assignable to type 'NonNullable<Cbs<T>[P]>'.
for (const cb of arr) { // TS2488: Type 'Cbs<T>[P]' must have a '[Symbol.iterator]()' method that returns an iterator.
}
for (const cb2 of nonNullArr) { // No error
}
}
}
Expected behavior: Compiles without errors.
Actual behavior: Several errors relating to arr not narrowing out the undefined
portion of the argument:
Error:(14, 15) TS2322: Type '((...args: T[P]) => boolean | Promise<boolean>)[] | undefined' is not assignable to type 'NonNullable<Cbs<T>[P]>'.
Type 'undefined' is not assignable to type 'NonNullable<Cbs<T>[P]>'.
Error:(16, 26) TS2488: Type 'Cbs<T>[P]' must have a '[Symbol.iterator]()' method that returns an iterator.
Related Issues:
#1726 (closed by design, this is different because we can assume any array is not undefined)
#31456 (this example has a constraint)