【问题标题】:Check if generic array includes a value检查泛型数组是否包含值
【发布时间】:2021-03-27 20:33:22
【问题描述】:

具有泛型和对象的条件类型可以正常工作,例如:

interface Bar<T extends Record<string, any>> {
    x: T extends { x: number } ? number : string,
    y: T extends { y: number } ? number : string,
    z: T extends { z: number } ? number : string,
}

const bar: Bar<{x: 1, y: 2, z: ""}> = {
    x: 1,
    y: 2,
    z: 3 // expected error, z supposed to be a string
}

如何将相同的原理应用于泛型数组?也就是说,我如何检查泛型是否包含值?我想要类似的东西

interface Foo<T extends number[]> {
    x: T includes 1 ? number : string,
    y: T includes 2 ? number : string,
    z: T includes 3 ? number : string,
}

const foo: Foo<[1,2]> = {
    x: 1,
    y: 2,
    z: ""
}

但显然 T includes x 不是有效的 TypeScript 语法。这甚至可能吗?

我已经尝试过:

x: T includes 1 ? number : string,
x: T includes [1] ? number : string,
x: T includes Array<1> ? number : string,

它们都不起作用

Playground example

我可以通过使用T[0] extends 1 和类似方法检查类型参数的特定索引处的特定值来接近我想要的:

interface Foo<T extends number[]> {
    x: T[0] extends 1 ? number : string,
    y: T[1] extends 2 ? number : string,
    z: T[2] extends 3 ? number : string,
}

const foo: Foo<[1,2]> = {
    x: 1,
    y: 2,
    z: 3 // expected error, the type argument doesn't have a number at index 2, so `z` is type string
}

const foo2: Foo<[1,2,4]> = {
    x: 1,
    y: 2,
    z: 3 // expected error, the type argument has 4 at index 2, not 3, so `z` is type string
}

Playground example

但这取决于顺序。我可以在不依赖订单的情况下做到这一点吗?所以z必须是number,只有当类型参数在它的任何地方都有3,否则string

【问题讨论】:

  • 恐怕我真的很难弄清楚你想要做什么。情况似乎并不相似。在您的对象示例中,不同的属性可以有不同的类型,但在您的数组示例中,Textends number[],因此T 中的条目必须是数字。您希望何时让xyz 改为字符串?
  • 如果您想查看通用参数 [1, 2] 是否包含 value 1 anywhere 在其中,我是 99 %确定你不能在类型系统中这样做。您可以测试类型参数的各个元素,但顺序很重要。
  • 你为什么使用元组类型而不是像1 | 2这样的联合?
  • 我已删除我的答案并将其折叠到问题中。如果您不喜欢我的更改,请随时将其回滚。将答案复制到问题中不是提出问题的人应该做的事情,但如果发布答案的人这样做也没关系 - 只要发布问题的人也可以。 :-) 我这样做是因为它似乎对这个问题有所帮助,而且我知道我无法进一步提出解决方案。
  • @jcalz - 我怎么没想到?! (不要回答这个问题,我们都知道为什么。:-))Like this?

标签: typescript typescript-generics


【解决方案1】:

作为jcalz pointed out(我没想到,我的 TypeScript 还不够好),您可以使用联合类型而不是元组来做到这一点:

interface Foo<T extends number> {
    x: T extends 1 ? number : string,
    y: T extends 2 ? number : string,
    z: T extends 3 ? number : string,
}

const foo: Foo<1|2> = {
    x: 1,
    y: 2,
    z: 3 // expected error, the type argument doesn't have 3
}

const foo2: Foo<2|1|3> = {
    x: 1,
    y: 2,
    z: 3 // no error, the type argument has as 3 -- and the order doesn't matter
}

const foo3: Foo<2|1|"three"> = { // error, "three" doesn't extend number
    x: 1,
    y: 2,
    z: 3
}

Playground link

【讨论】:

    猜你喜欢
    • 2019-08-10
    • 2015-01-01
    • 2021-07-15
    • 1970-01-01
    • 2016-02-04
    • 1970-01-01
    • 1970-01-01
    • 2021-06-06
    • 1970-01-01
    相关资源
    最近更新 更多