【问题标题】:Why can't use function to assert that var is not null in typescript为什么不能使用函数来断言 var 在打字稿中不为空
【发布时间】:2021-03-04 23:13:40
【问题描述】:

我在玩打字稿,发现了一些意想不到的东西。 我可以在showCarInfo2 函数中看到一个错误

为什么我不能对非空断言使用函数?

在第一个函数showCarInfo1 中有car.passengers !== null,一切正常。

interface Car {
  name: string;
  passengers: string[] | null;
}

const car: Car = {
  name: 'Seat',
  passengers: ['Andrew', 'Kate'],
}

function showCarInfo1(car: Car) {
  if(car.passengers !== null) {
    console.log(`${car.name}${car.passengers.map(passenger => ` ,${passenger}` )}`)
  } else {
    console.log(car.name)
  }
}

showCarInfo1(car)

const hasPassengers = (car: Car) => car.passengers !== null;

function showCarInfo2(car: Car) {
  if(hasPassengers(car)) {
    console.log(`${car.name}${car.passengers.map(passenger => ` ,${passenger}` )}`)
  } else {
    console.log(car.name)
  }
}

【问题讨论】:

  • 在大多数情况下,TypeScript 中的检查是不可传递的。如果你这样做了,例如x = foo !== null; if (x) { foo++},你仍然会得到一个错误。这是一个常见问题,也是一个已知问题。遍历传递条件并通过它们推断类型显然对编译器来说非常昂贵。
  • @VLAZ 是否有任何好的解决方案,或者我是否应该始终使用 `car.passengers !== null` 之类的断言,即使我必须在多个地方这样做?
  • 您可以创建一个user defined type-guard。但是您还需要一些围绕它的支持基础设施。我会尝试发布答案。

标签: typescript


【解决方案1】:

编译器不会跨函数边界执行control flow analysis。虽然如果编译器可以这样做会很好,但它会非常昂贵。一般来说,编译器必须先模拟程序运行的所有可能方式,然后才能得出关于类型的结论,并且假设您希望程序在宇宙热死之前编译,将会有限制。有关此问题的详细讨论,请参阅 microsoft/TypeScript#9998,“控制流分析中的权衡”。

如果这种情况不会自动发生,您可以使用一种技术告诉编译器 hasPassengers 的意图充当其参数的类型保护。您可以将其注释为user-defined type guard function。返回类型是arg is Type 形式的类型谓词,它是boolean 的特殊子类型(因此用户定义的类型保护仅适用于返回boolean 的函数):

interface NonNullCar extends Car {
  passengers: string[];
}

const hasPassengers = (car: Car): car is NonNullCar => car.passengers !== null;

如果您进行上述更改,您的代码将按需要编译:

function showCarInfo2(car: Car) {
  if (hasPassengers(car)) {
    console.log(`${car.name}${car.passengers.map(passenger => ` ,${passenger}`)}`) // no error
  } else {
    console.log(car.name)
  }
}

Playground link to code

【讨论】:

    【解决方案2】:

    您可以创建user-defined type guard 来检查对象的任意属性是否存在。为此,您需要一个支持的泛型类型:

    type HasProperty<T, K extends keyof T> = T & { [P in K]-?: Exclude<T[K], null | undefined> };
    

    这是一个泛型类型,它接受任何东西,以及它的一个键 K 作为泛型,并且与 Exclude utility typethe -? mapped type modifier 构造一个具有相同键的类型,除了:

    • 总是有K
    • K 永远不是 null
    • K 永远不是 undefined

    本质上

    type CarWithPassangers = HasProp<Car, "passengers">
    

    喜欢定义

    interface CarWithPassangers {
      name: string;
      passengers: string[]
    }
    

    使用这种类型,您可以创建一个泛型类型保护:

    const hasProperty = <T, K extends keyof T>(obj: T, prop: K) : obj is HasProperty<T, K>  =>
        prop in obj 
            && obj[prop] !== null
            && obj[prop] !== undefined;
    

    最后像这样使用它:

    function showCarInfo(car: Car) {
      if(hasProperty(car, "passengers")) {
        console.log(`${car.name}${car.passengers.map(passenger => ` ,${passenger}` )}`)
      } else {
        console.log(car.name)
      }
    }
    

    Playground Link

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-16
      • 2016-06-14
      • 2022-09-29
      • 2021-01-03
      • 2014-10-06
      • 2021-08-13
      • 1970-01-01
      相关资源
      最近更新 更多