避免推断为数组元素的最小公分母 [英] Avoid inferring as least common denominator of array elements

查看:26
本文介绍了避免推断为数组元素的最小公分母的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果一个函数有一个泛型参数 并接受一个与 T 相关的项目数组,那么 T 似乎总是推断为所有元素的交集,即使 T 也可以从函数的返回类型推断出来.示例:

If a function has a generic parameter <T> and takes an array of items related to T, it seems T is always inferred as the intersection of all elements, even if T could also be inferred from the return type of the function. Example:

type Item = {foo: string, bar: number};

// A List<Item> may contain items containing partial Items such as {foo: 'FOO'}, {bar: 42}
interface List<T> {
  items: Array<Partial<T>>;
}

function list<T>(items: Array<Partial<T>>): List<T> {
  return {items};
}

class Baz<T> {
  bar(list: List<T>) {
  }
}

现在,如果我创建一个 Bar,它的 bar 方法需要一个 List.但是,如果我使用便利函数 list(),即使它返回 ListT 也不会被推断为 Item,每个item都是Partial;相反,T 被推断为所有数组元素的交集,这意味着它基本上会失败,除非所有元素都包含相同的属性.

Now, If I create a Bar<Item>, its bar method wants a List<Item>. However, if I use the convenience function list(), even though it returns List<T>, T is not inferred as Item, with each item being Partial<Item>; instead T is inferred as the intersection of all array elements, meaning it basically fails unless all elements contain the same properties.

这意味着以下内容无法编译:

This means that the following does not compile:

new Baz<Item>().bar(list([{foo: 'FOO', bar: 42}, {bar: 42}]))

-> TS2322: 'foo' does not exist in Partial<{bar: number}>

这里,T 被推断为 Partial<{bar: number},这是两个元素的交集.

Here, T was inferred as Partial<{bar: number} which is the intersection of both elements.

以下有效,因为将 undefined 添加到第二个元素会扩大推断的 T 成为 {foo: string;酒吧:数字} |{foo:未定义;条:数字}:

The following works since adding undefined to the second element widens the inferred T to become {foo: string; bar: number} | {foo: undefined; bar: number}:

new Baz<Item>().bar(list([{foo: 'FOO', bar: 42}, {bar: 42, foo: undefined}]))

以下当然也有效,因为 T 根本不需要推断:

The following of course also works, since T does not have to be inferred at all:

new Baz<Item>().bar(list<Item>([{foo: 'FOO', bar: 42}, {bar: 42}]))

问题:有没有办法定义一个具有泛型参数 T 并获取与 T 相关的数组元素的函数,以便 T 是从返回类型而不是数组元素的交集推断,换句话说,允许不相交的数组元素?

Question: Is there a way to define a function having a generic parameter T and taking array elements related to T so that T is inferred from the return type instead of the intersection of the array elements, in other words allowing non-intersecting array elements?

推荐答案

我相信我们与 过多的属性检查 .

下一个数组 [{ foo: 'FOO', bar: 42 }, { bar: 42 }] 被推断为:

Next array [{ foo: 'FOO', bar: 42 }, { bar: 42 }] is infered to :

({
    foo: string;
    bar: number;
} | {
    bar: number;
    foo?: undefined;
})[]

但在我们的例子中,我们有泛型的函数.因此,T 被推断为 {bar: number} 的原因与 :

But in our case we have function with generic. Hence, T is infered to {bar: number} for the same reason as :

type A = { foo: string, bar: number };
type B = { bar: number };
type C = A | B;

type Keys = keyof C // 'bar'

因为 bar 是两个联合之间的共享属性.

Because bar is a shared property between both unions.

我们可以通过这种方式跳过多余的属性检查:

We can skip excess property checking in this way:

type Item = { foo: string, bar: number };

// A List<Item> may contain items containing partial Items such as {foo: 'FOO'}, {bar: 42}
interface List<T> {
    items: Array<Partial<T>>;
}

function list<T>(items: Array<Partial<T>>): List<T> {
    return { items };
}

class Baz<T> {
    bar(list: List<T>) {
    }
}

const arr = [{ foo: 'FOO', bar: 42 }, { bar: 42 }];


const result = new Baz<Item>().bar(list(arr))

只需将数组分配给变量即可.

Just assign array to the variable.

更好的选择

只需添加额外的通用:

type Item = { foo: string, bar: number };

// A List<Item> may contain items containing partial Items such as {foo: 'FOO'}, {bar: 42}
type List<T> = {
    items: Array<Partial<T>>;
}

// extra generic here
function list<T, A extends Array<Partial<T>>>(items: A): List<T> {
    return { items }
}


class Baz<T> {
    bar<U extends T>(list: List<U>) {
    }
}

const result = new Baz<Item>().bar(list([{ foo: '1', bar: 2 }, { bar: 2 }])) // ok

这篇关于避免推断为数组元素的最小公分母的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆