查找数组元素类

时间:2017-08-08 11:53:14

标签: typescript

我有一个基本的方法:

function foo<T>(data:any, clazz:new(...args:any[])=>T):T{
    //Example impl, just to demonstrate the issue
    if(data instanceof Array){
        return new clazz();
    }
    return [new clazz()];
}

所以,我希望它能同时使用对象和数组,如下所示:

let bar:Bar = foo<Bar>(Bar); //Works fine actually
let bars:Bar[] = foo<Bar[]>(Bar); //Should return a Bar[]

它不起作用,因为Bar不是Bar[]的构造函数。

我已经尝试过:

foo<Bar[]>(Bar[])
foo<Bar[]>(Array<Bar>)
foo<Array<Bar>>(Array<Bar>)

2 个答案:

答案 0 :(得分:1)

你要求的是,

let bar:Bar = foo<Bar>(Bar); //Works fine actually
let bars:Bar[] = foo<Bar[]>(Bar); //Should return a Bar[]

永远不会工作,因为类型会在运行时被删除。两者的JavaScript将如下所示:

let bar = foo(Bar);
let bars = foo(Bar);

所以没有办法分辨出来。

正如@AlekseyL所暗示的,最直接的解决方案是有两个函数来处理数组/非数组的情况。如果真的想要一个函数,我们需要有一种方法让它在运行时知道该怎么做。

首先,让我们创建帮助器Constructor类型,因为我讨厌一直写出new(...内容:

type Constructor<T> = {
  new(...args: any[]): T;
}

现在让我们创建一个ArrayOf类型,它只是一个包含相关类构造函数的对象。我们还需要一个类型保护来检测某些东西在运行时是否为ArrayOf,还有一个函数来生成开发人员可以使用的ArrayOf

type ArrayOf<T> = {
  clazz: Constructor<T>;
}
function isArrayOf<T>(x: Constructor<T> | ArrayOf<T>): x is ArrayOf<T> {
  return 'clazz' in x;
}
function ArrayOf<T>(clazz: Constructor<T>): ArrayOf<T> {
  return { clazz };
}

最后,我们可以实施foo()

function foo<T>(clazz: Constructor<T>): T;
function foo<T>(arrayOfClazz: ArrayOf<T>): T[];
function foo<T>(x: Constructor<T> | ArrayOf<T>): T | T[] {
  if (isArrayOf(x)) {
    return [new x.clazz()];
  } else {
    return new x();
  }
}

这是一个重载函数,它接受构造函数或ArrayOf对象,然后确定在运行时要做什么。让我们确保它有效:

class Bar {
  // ... 
}

const bar: Bar = foo(Bar);
const bars: Bar[] = foo(ArrayOf(Bar));

你走了;它有效!

但与仅拥有两个功能相比,这相当复杂。对于开发人员来说,所有这些都是大致相同的努力:

  // what you wanted
  let bar = foo<Bar>(Bar); 
  let bars = foo<Bar[]>(Bar);  // will not work, as above 

  // what I gave you
  let bar = foo(Bar);
  let bars = foo(ArrayOf(Bar));

  // two functions
  let bar = foo(Bar);
  let bars = fooArray(Bar);

我认为双功能解决方案可能是最开发的,但这只是我的观点。无论如何,希望有所帮助。祝你好运!

更新

嗯,我看到你编辑了你的代码,为data添加了一个新的foo参数,这个参数是一个数组与否,你想要根据它返回一个数组。在这种情况下,解决方案非常简单:

function foo<T>(data: any[], clazz: Constructor<T>): T[];
function foo<T>(data: any, clazz: Constructor<T>): T;
function foo<T>(data: any, clazz: Constructor<T>): T | T[] {
  if (Array.isArray(data)) {
    return [new clazz()];
  } else {
    return new clazz();
  }
}

const bar: Bar = foo('bar',Bar);
const bars: Bar[] = foo(['bars'], Bar);

祝你好运。

答案 1 :(得分:1)

您可以使用签名重载并根据传递的参数决定返回数组或单个实例:

type Constructor<T> = new () => T;

function foo<T>(clazz: [Constructor<T>]): T[]
function foo<T>(clazz: Constructor<T>): T
function foo<T>(clazz: Constructor<T> | [Constructor<T>]) {
    if(clazz instanceof Array){
        return [new clazz[0]];
    }
    return new clazz();
}

let arrayResult = foo([Bar]);
let result = foo(Bar);