[ember] refine types for Ember.tryInvoke (#28693)

* [ember] refine types for Ember.tryInvoke

* [ember] tryInvoke as three function heads
This commit is contained in:
Mike North
2018-09-10 18:47:45 -07:00
committed by Ryan Cavanaugh
parent 2a2f49e0d0
commit 59f25426d5
2 changed files with 50 additions and 1 deletions

View File

@@ -28,6 +28,20 @@ declare module 'ember' {
// Get an alias to the global Array type to use in inner scope below.
type GlobalArray<T> = T[];
// TODO: TypeScript 3.0
// type FunctionArgs<F extends (...args: any[]) => any> = F extends (...args: infer ARGS) => any ? ARGS : never;
type FunctionArgs<F> =
F extends (a: infer A) => any
? [A]
: F extends (a: infer A, b: infer B) => any
? [A, B]
: F extends (a: infer A, b: infer B, c: infer C) => any
? [A, B, C]
: F extends (a: infer A, b: infer B, c: infer C, d: infer D) => any
? [A, B, C, D]
: F extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E) => any
? [A, B, C, D, E]
: never;
/**
* Deconstructs computed properties into the types which would be returned by `.get()`.
*/
@@ -3320,7 +3334,14 @@ declare module 'ember' {
* Checks to see if the `methodName` exists on the `obj`,
* and if it does, invokes it with the arguments passed.
*/
function tryInvoke(obj: any, methodName: string, args?: any[]): any;
function tryInvoke<FNAME extends keyof T, T extends object>(
obj: T,
methodName: FNAME,
args: FunctionArgs<T[FNAME]>): T[FNAME] extends ((...args: any[]) => any)
? ReturnType<T[FNAME]>
: undefined;
function tryInvoke<FNAME extends keyof T, T extends object>(obj: T, methodName: FNAME): T[FNAME] extends (() => any) ? ReturnType<T[FNAME]> : undefined;
function tryInvoke(obj: object, methodName: string, args?: any[]): undefined;
/**
* Forces the passed object to be part of an array. If the object is already
* an array, it will return the object. Otherwise, it will add the object to

View File

@@ -99,3 +99,31 @@ function testDefineProperty() {
return `${this.firstName} ${this.lastName}`;
}));
}
function testTryInvoke() {
class Foo {
hello() { return ['world']; }
add(x: number, y: string) { return x + parseInt(y, 10); }
apples(n: number) { return `${n} apples`; }
}
// zero-argument function
Ember.tryInvoke(new Foo(), 'hello'); // $ExpectType string[]
// one-argument function
Ember.tryInvoke(new Foo(), 'apples', [4]); // $ExpectType string
// multi-argument function with different types (reversed types negative test case below)
Ember.tryInvoke(new Foo(), 'add', [4, '3']); // $ExpectType number
// Cases that should return undefined
// No args provided
Ember.tryInvoke(new Foo(), 'apples'); // $ExpectType undefined
// Function does not exist
Ember.tryInvoke(new Foo(), 'doesNotExist'); // $ExpectType undefined
// Empty args provided
Ember.tryInvoke(new Foo(), 'apples', []); // $ExpectType undefined
// Wrong args provided
Ember.tryInvoke(new Foo(), 'apples', ['']); // $ExpectType undefined
// Wrong arg types
Ember.tryInvoke(new Foo(), 'add', [4, 3]); // $ExpectType undefined
// Reversed arg types
Ember.tryInvoke(new Foo(), 'add', ['4', 3]); // $ExpectType undefined
}