【问题标题】:Extend either item type or type for items in an array为数组中的项目扩展项目类型或类型
【发布时间】:2020-01-11 09:17:11
【问题描述】:

我有一个处理一些实体并最终向它们添加属性的函数。它非常灵活,可以处理不同类型的值:属性可以分配给对象,或者如果它是一个数组,则可以分配给数组中的每个对象(实际的东西要复杂得多,不能拆分成不同的函数)。最后,它扩展了传入类型并返回具有少量属性的扩展类型。所以它看起来像这样:

function addSomeStuff<T>(data): TypeWithAddedProperty<T> {
    if (Array.isArray(data)) {
        return data.map(element => {
            const newElement = { ...element, addedProperty: 'something' }
            return newElement;
        })
    } else {
        const newElement = { ...data, addedProperty: 'something' }
        return newElement;
    }
}
export type TypeWithAddedProperty<T> = T & {addedProperty: string};

所以问题是:TypeWithAddedProperty 可以是有条件的,所以它会支持那里的数组吗?现在,如果我在里面传递一个数组,我将得到 TypeWithAddedProperty&lt;Array&lt;T&gt;&gt; 而不是实际的 Array&lt;TypeWithAddedProperty&lt;T&gt;&gt;。

【问题讨论】:

    标签: javascript typescript types


    【解决方案1】:

    如果我理解正确,您可以通过函数重载实现您所需要的:

    这个想法是定义addSomeStuff当给定一个数组时返回一个数组,当给定一个常规值时返回一个常规值。

    function addSomeStuff<T>(data: T[]):TypeWithAddedProperty<T>[];
    function addSomeStuff<T>(data: T):TypeWithAddedProperty<T>;
    
    function addSomeStuff<T>(data: T|T[]): TypeWithAddedProperty<T>|TypeWithAddedProperty<T>[] {
        if (isArray(data)) {
            return data.map((element:T) => {
                const newElement = { ...element, addedProperty: 'something' }
                return newElement as TypeWithAddedProperty<T>;
            })
        } else {
            const newElement = { ...data, addedProperty: 'something' }
            return newElement;
        }
    }
    export type TypeWithAddedProperty<T> = T & {addedProperty: string};
    
    
    
    const a : TypeWithAddedProperty<{}> = addSomeStuff({}); // works
    const b : TypeWithAddedProperty<{}>[] = addSomeStuff([ {} ]); // works
    
    const c : TypeWithAddedProperty<{}>[] = addSomeStuff({}); // error 
    

    【讨论】:

      【解决方案2】:

      我认为你可以用更通用的方式来做到这一点。首先,声明一个类型为T 或T 的任意嵌套数组:

      interface Subtree<T> extends Array<T | Subtree<T>> { }
      type Tree<T> = T | Subtree<T>;
      

      (详见Describe a deeply nested array in TypeScript)。

      现在定义一个接受Tree&lt;T&gt;的函数和一个函数T-&gt;U并返回Tree&lt;U&gt;:

      function fmap<T, U>(fn: (arg: T) => U) : (arg: Tree<T>) => Tree<U> {
          return function(x: Tree<T>) {
              if (Array.isArray(x)) {
                  return x.map(fmap(fn))
              } else {
                  return fn(x)
              }
          }
      }
      

      现在您可以执行以下操作:

      type Foo = { foo: number };
      type FooBar = Foo & { bar: string };
      
      let addBar = fmap((x: Foo) => ({...x, 'bar': 'hey'}));
      
      let data = [{foo: 1}, [{foo: 2}, [{foo: 3}]]];
      let res = addBar(data); // checks as Tree<FooBar>
      

      Playground

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-09
        • 1970-01-01
        • 1970-01-01
        • 2019-05-14
        • 1970-01-01
        • 1970-01-01
        • 2011-10-17
        • 1970-01-01
        相关资源
        最近更新 更多