如何告诉 TypeScript 理解在 if 子句中使用 array.some() 已经缩小了类型范围?

How to tell TypeScript to understand that using array.some() in an if clause already narrows type?

提问人:Ooker 提问时间:9/20/2023 更新时间:9/20/2023 访问量:30

问:

我有这个代码:

const list = ['a', 'b', '= c', 'd']
if (list.some(element => element.includes('='))) {
    const elementWithEqualSign = list.find(element => element.includes('='))
}

这里有类型,而实际上它应该只是,由于上面的条件过滤了的可能性。有没有办法告诉它在不使用类型断言的情况下自动缩小类型范围?我想答案就在 TypeScript 的某个地方:文档 - 缩小范围,但我不知道要找出它。elementWithEqualSignstring | undefinedstringundefinedas string

TypeScript if 语句 类型缩小

评论


答:

2赞 Roger Lipscombe 9/20/2023 #1

在一般情况下,不可以。

考虑以下情况:您在 中使用与 中的条件不同的条件。.some().find()

编译器如何(自动)确定第一个条件是否更改了 的返回类型。.find()

如果条件非常复杂怎么办?例如,如果它根据 REST API 检查每个元素,会怎么样?即使具有看似相同的条件,它也可能在第二次返回不同的结果(这有时称为检查时间-使用时间-TOCTOU),这意味着这是一种可能的类型。undefined

使用类型断言。

评论

0赞 Ooker 9/20/2023
如果我使用相同的回调,并且它只有一种类型的输出怎么办?
0赞 mbojko 9/20/2023
类型断言非 null 断言运算符:。const elementWithEqualSign = list.find(element => element.includes('='))!;
1赞 Dimava 9/20/2023 #2

好吧,不要这样做,但
https://tsplay.dev/mLJBZw

interface Array<T> {
  // make "some" to say it has the item
  some<V>(predicate: (v: T) => v is T & V): this is { guaranteedHas: V };
  // make "find" to get the item if it has it
  find<V>(this: { guaranteedHas: V }, predicate: (v: T) => v is T & V): V;
}
interface ReadonlyArray<T> {
  // make "some" to say it has the item
  some<V>(predicate: (v: T) => v is T & V): this is { guaranteedHas: V };
  // make "find" to get the item if it has it
  find<V>(this: { guaranteedHas: V }, predicate: (v: T) => v is T & V): V;
}
interface String {
  // this is a "proper" typing, but...
  includes<T, V extends string>(this: T, searchString: V): this is T & `${string}${V}${string}`;
}

function includes<D extends string>(d: D) {
  // ...but you need a wrapper because `is` returns become booleans
  return function inc<S extends string>(s: S): s is S & `${string}${D}${string}` {
    return s.includes(d)
  }
}

const list = ['a', 'b', '= c', 'd'] as const
if (list.some(includes('='))) {
  const elementWithEqualSign = list.find(includes('='))
  //    ^?
  // const elementWithEqualSign: "= c"
}
0赞 mbojko 9/20/2023 #3

为什么要用相同的测试遍历两次阵列?两者均可用于:find

const elementWithEqualSign = list.find(element => element.includes('='));

if (elementWithEqualSign) {
// elementWithEqualSign is a string here
}

评论

0赞 Ooker 9/20/2023
当我使用相同的想法但对于正则表达式字符串时,我被告知它的运行速度比 .因此,虽然您可以使用 result of 作为条件子句,但建议将它们分开。我在这里应用这个概念test()match()match
0赞 mbojko 9/20/2023
@Ooker 对于 VS ,性能差异似乎可以忽略不计:measurethat.net/Benchmarks/Show/12534/0/arrayfind-vs-arraysomefindsome
0赞 Ooker 9/20/2023
事情什么时候开始变得不可思议?因为如果它真的可以忽略不计,那么我们根本不需要使用,对吧?some()