我正在try 缩小泛型的范围,其中自动补全检测到它正在命中类型保护,因此不会再次到达相同的代码块.我认为原因是我没有将类型限制为泛型,但我不确定我将如何做到这一点,这是可能的吗?感觉这应该是可能的,但我不确定.任何帮助都将不胜感激.

// Setup
export type Feature<Geometry> = {
  type: 'Feature',
  geometry: Geometry
}

type Geometry = Point | Curve

interface Base {
  type: string
}

interface Point extends Base{
  type: 'Point'
}

interface Curve extends Base {
  type: 'Curve'
}

// Typeguard
function isGeometry<G extends Geometry, U extends G['type']>(geometry: G, disciminator: U): geometry is Extract<G, {type: U}>{
  return geometry.type === disciminator
}


function isFeature<G extends Geometry, U extends G['type']>(feature: Feature<G>, disciminator: U): feature is Feature<Extract<G, {type: U}>> {
  return feature.geometry.type === disciminator
}

function whatGeometry(feature: Feature<Point | Curve>) {
  if(isGeometry(feature.geometry, 'Curve')){
    return feature.geometry;
                   // ^?
  }
  if(isGeometry(feature.geometry, 'Point')){
    return feature.geometry;
                    // ^?
  } // Autocompletes, and knows that we can't have anything else for a geometry, 
  return;
}

function whatFeature(feature: Feature<Point | Curve>) {
    if(isFeature(feature, 'Curve')){
      return feature.geometry;
              // ^?
    }
    if(isFeature(feature, 'Point')) {
      return feature;
              // ^?
    } // Assumes we can have another Feature<Point> even though the upper typeguard should have caught it

    return;
}

Playground

推荐答案

您面临的主要问题是,像isFeature这样的user-defined type guard functions只允许您指定在它们返回true时应该发生什么,这就是将相关参数的类型限制为受保护的类型.因此,对于返回类型为arg is Type的函数,我们知道,对于true的结果,arg将缩小到类似typeof arg & Type的范围,或者Extract<typeof arg, Type>(使用Extract utility type),这取决于arg的类型.粗略地说,如果typeof argunion type,那么你会得到类似Extract的行为;否则,你会得到类似intersection的行为.(我在这里经常说"Like",因为实际的行为有点微妙,我不想离题太远.)

但是当函数返回false时,编译器必须try 确定自己要做什么.不存在microsoft/TypeScript#15048中要求的"单边"或"细粒度"类型的保护函数,可以让您 Select arg is TrueType else FalseType或类似的函数.现在发生的情况是,如果arg有一个联合类型,那么你会得到类似Exclude<typeof arg, Type>的东西,它实际上过滤了arg.但是,如果arg没有联合类型,那么您只会得到typeof arg.TypeScrip缺少negated typesnot Type,所以在false中没有typeof arg & not Typetypeof arg & Type这样的类型可以缩小到范围.(在microsoft/TypeScript#29317中有一个否定类型的实现,但它从未被采用.)因此,编译器通常最多只能忽略arg.


这直接导致了你在isFeature岁时看到的行为.输入类型Feature<Point | Curve>不是联合类型,因此当isFeature返回FALSE时,编译器对输入类型的操作不多.

如果您希望能够缩小到false分支的范围,则应该考虑将输入类型设置为像Feature<Point> | Feature<Curve>这样的联合类型.这样做要复杂得多,因为您当前的isFeature()会对这种类型的输入犹豫不决.但您可以重构以允许unions 输入,可能如下所示:

function isFeature<T extends Geometry['type'], U extends T>(
  feature: Feature<Geometry & { type: T }>, disciminator: U
): feature is Feature<Geometry & { type: U }> {
  return feature.geometry.type === disciminator
}

function whatFeature(feature: Feature<Point> | Feature<Curve>) {
  if (isFeature(feature, 'Curve')) {
    return feature.geometry;
           // ^?(parameter) feature: Feature<Curve>
  }
  feature;
  // ^?(parameter) feature: Feature<Point>
  if (isFeature(feature, 'Point')) {
    return feature;
    //     ^?(parameter) feature: Feature<Point>
  }
  return;
} 

因此,featureFeature<Point> | Feature<Curve>,这使得负类型保护结果可以缩小它的范围.而isFeature()是允许联合输入的type-T中的generic.当它返回true时,然后编译器将输入缩小到几何类型为U的那些联合成员,因此false结果将输入缩小到这些成员的补数.

这就提供了您想要的行为,在对isFeature(feature, 'Curve')进行负判断后,将feature缩小到Feature<Point>,这是您所希望的.

Playground link to code

Typescript相关问答推荐

TypScript ' NoInfer '类型未按预期工作

忽略和K的定义

找不到带名称的管道''

如何缩小变量访问的对象属性范围?

如何使用一个字段输入对象,其中每个键只能是数组中对象的id属性,而数组是另一个字段?

状态更新后未触发特定元素的Reaction CSS转换

如何避免推断空数组

TypeScrip:从对象中提取和处理特定类型的键

找不到财产的标准方式?

从route.参数获取值.订阅提供";无法读取未定义";的属性

在打字脚本中对可迭代对象进行可变压缩

在打字应用程序中使用Zod和Reaction-Hook-Forms时显示中断打字时出错

如何创建由一个帐户签名但使用另一个帐户支付的Hedera交易

React-跨组件共享功能的最佳实践

Svelte+EsBuild+Deno-未捕获类型错误:无法读取未定义的属性(读取';$$';)

如何向我的自定义样式组件声明 custom.d.ts ?

我应该使用服务方法(依赖于可观察的)在组件中进行计算吗?

当受其他参数限制时,通用参数类型不会缩小

基于泛型的柯里化函数的条件参数

Angular 外部订阅值 Null