2016-08-04 52 views
3

我正在转换一些我用于打字的东西,而且我有点遗憾。我做了一个通用的foreach函数,它接受数组和对象,并采用几种不同类型的回调来处理迭代。但是,如果接受几个不同的回调函数,我不知道如何指定类型。我需要能够从函数返回布尔值或无效,并且它应该能够传入(any),(any,int)或(string,any)。这是我到目前为止所做的。Typescript:在工会中指定多个回调类型

function foreach(obj : Array<any> | Object, func : (
      ((any) => boolean) | 
      ((any) => void) | 
      ((any, int) => boolean) | 
      ((any, int) => void) | 
      ((string, any) => boolean) | 
      ((string, any) => void) 
)) 
{ 
    // if obj is an array ... 
    if(Object.prototype.toString.call(obj) === '[object Array]') { 
     // if callback def1 
     if(func.length == 1) { // error: property length does not exist on type 'any[] | Object' 
      for(let i = 0; i < obj.length; i++) { 
       if(typeof func === "function") { 
        if(!func(obj[i])) break; // error: Cannot invoke an expression whose type lacks a call signature 
       } 
      } 
     // if callback def2 
     } else if(func.length == 2) { // error: property length does not exist on type 'any[] | Object' 
      for(let i = 0; i < obj.length; i++) { 
       if(!func(obj[i], i)) break; // error: Cannot invoke an expression whose type lacks a call signature 
      } 
     } 
    // if obj is an object ... 
    } else if(Object.prototype.toString.call(obj) == '[object Object]') { 
     // if callback def1 
     if(func.length == 1) { 
      for(let key in obj) { 
       if(!obj.hasOwnProperty(key)) continue; 
       if(!func(obj[key])) break; // error: Cannot invoke an expression whose type lacks a call signature 
      } 
     // if callback def3 
     } else if(func.length == 2) { 
      for(let key in obj) { 
       if(!obj.hasOwnProperty(key)) continue; 
       if(!func(key, obj[key])) break; // error: Cannot invoke an expression whose type lacks a call signature 
      } 
     } 
    } 
}; 
+0

@SantiagoHernándezfunc.length是javascript函数的一个属性,它是该函数的参数数量。这是我最初区分原始JavaScript中的函数签名的方式。问题是我该如何在打字稿中做到这一点:\ – FatalCatharsis

回答

1

你去:使用的

function foreach<T>(obj : T[], func: (item: T) => void) 
function foreach<T>(obj : T[], func: (item: T) => boolean) 
function foreach<T>(obj : T[], func: (item: T, index: number) => boolean) 
function foreach<T>(obj : T[], func: (item: T, index: number) => void) 
function foreach<T>(obj : {[key: string]: T}, func: (item: T) => boolean) 
function foreach<T>(obj : {[key: string]: T}, func: (item: T) => void) 
function foreach<T>(obj : {[key: string]: T}, func: (key: string, item: T) => boolean) 
function foreach<T>(obj : {[key: string]: T}, func: (key: string, item: T) => void) 
function foreach<T>(obj : T[] | {[key: string]: T}, func : (item : T | string, index ?: number | T) => (boolean | void)) 
{ 
    if(Object.prototype.toString.call(obj) === '[object Array]') { 
     let arr = <any>obj as T[]; 
     if(func.length == 1) { 
      let cb = <any>func as (item: T) => boolean; 
      for(let i = 0; i < arr.length; i++) if(!cb(arr[i])) break; 
     } else if(func.length == 2) { 
      let cb = <any>func as (item: T, index: number) => boolean; 
      for(let i = 0; i < arr.length; i++) if(!cb(obj[i], i)) break; 
     } 
    } else if(Object.prototype.toString.call(obj) == '[object Object]') { 
     let arr = obj as {[key: string]: T}; 
     if(func.length == 1) { 
      let cb = <any>func as (item: T) => boolean; 
      for(let key in obj) { 
       if(!obj.hasOwnProperty(key)) continue; 
       if(!cb(obj[key])) break; 
      } 
     } else if(func.length == 2) { 
      let cb = func as (key: string, item: T) => boolean; 
      for(let key in obj) { 
       if(!obj.hasOwnProperty(key)) continue; 
       if(!cb(key, obj[key])) break; 
      } 
     } 
    } 
}; 

例如: enter image description here enter image description here

您可以智能感知的优势: enter image description here

+0

哇这是伟大的,只是我在找什么。 “ obj as T []”是什么意思,具体是部分?另外,这是多功能的东西来自打字稿吗?什么允许重载这样的语法? – FatalCatharsis

+0

也,这不是我自己编译。我相信这与这个问题所谈论的内容有关。 https://stackoverflow.com/questions/18168879/method-overload-not-working-for-me。似乎我需要第9次超载,它们都是超级套装:\ – FatalCatharsis

+0

Welp,我从来不知道[this](https://basarat.gitbooks.io/typescript/content/docs/types/functions.html #overloading)。比我预期的更优雅。您提供的代码唯一需要的更改是将超集函数添加到重载列表的底部,该列表不能在声明之外使用。添加这个到你的答案,它被接受:) 'function foreach (obj:T [] | {[key:string]:T},func:(item:T | string,index?:number | T)= >(boolean | void))' – FatalCatharsis

0

您可以通过采取简化代码通用func的优势(函数参数化类型,如function foo<T>),可选参数(使用?)和多个函数签名声明。这将让你避免所有这些工会和any的。

首先,把你的功能分成两部分是有意义的。原因是TypeScript不支持在运行时分派给不同实现的意义上的多态。你真的有两个不同的函数(一个用于数组,一个用于对象),所以你应该这样写。我知道jQuery甚至强调伪多态,但实际上这种看似便利是一个糟糕的设计原则。写下你的意思,并且表示你写的东西。

您的代码最终会看起来像:

function forEachArray<T>(
    array: Array<T>, 
    func?: (value: T, index?: number): boolean 
): void { 
    for (let i = 0; i < obj.length; i++) { 
    if (func && !func(array[i], i)) break; 
    } 
} 

function forEachObject<T>(
    object: {[index: string]: T}, 
    func?: (value: T, key?: string): boolean 
): void { 
    for (let key of Object.keys(object)) { 
    if (func && !func(object[key], key)) break; 
    } 
} 

这里,我们使用一对夫妇的工具,你似乎并不可能已经觉察到的。一个是问号?表示可选参数,我们既使用func本身也使用func(索引或键)的第二个参数。第二个是泛型,我们在这里使用它来强制数组或对象中值的同质性。如果你的数组包含字符串,例如,你传入的函数必须以字符串作为其第一个(值)参数。如果你想放松这个限制,你总是可以称之为forEachObject<any>(...

设计方面,回调返回boolean或void似乎有问题。既然你总是使用它,就好像它正在返回一个布尔值一样,强制执行它。

您不需要特殊情况下回调确实或不需要第二个参数的情况。您可以简单地在func回调参数的签名中使用?来选择该参数,然后继续传递它,如果需要,该函数将忽略它。

此外,为了与Array#forEach语义保持一致,您应该还可以允许使用可选的thisArgs第三个参数,并且还会为您的回调函数传递第三个参数,以表示底层数组或对象。在forEachArray的情况下,应该是这样的:

function forEachArray<T>(
    array: Array<T>, 
    func?: (value: T, index?: number, array?: Array<T>): boolean, 
    thisArg?: Object 
): void { 
    for (let i = 0; i < obj.length; i++) { 
    if (func && !func.call(thisArg, array[i], i, array)) break; 
    } 
} 

如果你真的想有一个foreach函数,它数组或对象,请使用定义多个函数签名的技术,然后是实现。我定义了一个Hash类型紧凑:

type Hash<T> = {[index: string]: T}; 

function foreach<T>(
    array: Array<T>, 
    func?: (value: T, index?: number, array?: Array<T>): boolean, 
    thisArg?: Object 
): void; 

function foreach<T>(
    object: Hash<T>, 
    func?: (value: T, key?: string, object?: Hash<T>): boolean, 
    thisArg?: Object 
): void; 

function foreach(thing, func?, thisArg?) { 
    (thing instanceof Array ? forEachArray : forEachObject)(thing, func, thisArg); 
} 

注意,声明多个函数签名这样的时候,你并不需要在执行指定任何类型的,如果你做了,他们将被忽略。

警告:以上代码均未经过测试甚至编译。

+1

为什么'func'在'foreach'中是可选的?这不是它的主要原因吗? –