首页 > 解决方案 > 如何断言泛型是 NonNullable

问题描述

我有一些方法用于NonNullable<T>允许某些部分假定在调用之前已执行空值检查。我还有一个函数可以在调用上述方法之前执行该检查。

我的问题是这个函数接受一个T我需要缩小到的泛型NonNullable<T>。在编写检查nullundefined值的保护代码时,这种类型不会通过推理自动缩小为不可为空的类型。

export interface UrlParam<T> {
  defaultValue: T,
  encode: (value: NonNullable<T>) => string
  decode: (value: string) => NonNullable<T>
}

interface LatLng {
  lat: number
  lng: number
}

const COORDINATE_DELIMITER = ','

const locationParam: UrlParam<LatLng | null> = {
  defaultValue: null,
  encode: ({ lat, lng }) => [lat, lng].join(COORDINATE_DELIMITER),
  decode: value => {
    const [lat, lng] = value.split(COORDINATE_DELIMITER)

    return {
      lat: Number(lat),
      lng: Number(lng),
    }
  }
}

function encodeParam<T>(urlParam: UrlParam<T>, value: T) {
  return value !== undefined && value !== null ? urlParam.encode(value) : null
}

encodeParam函数产生以下编译错误:

“T”类型的参数不能分配给“NonNullable”类型的参数

我可以通过强制 TypeScript 编译器将T其视为NonNullable<T>

urlParam.encode(value as NonNullable<T>)

但是,我正在寻找一种解决方案,其中编译器实际上可以根据条件本身推断出这种类型。这可能吗?我可能以错误的方式接近这个吗?

标签: typescript

解决方案


虽然有朝一日可能通过控制流分析而不是类型断言或使用非空断言运算符value as NonNullable<T>的 terser来做这种事情,但现在编译器不会自己做这件事。value!


相反,如果您想要编译器可以遵循的东西,我的建议是从“我需要删除的可能或类型”T更改为“null我可能需要删除的非和非类型”添加and 。您可以使用空类型来表示“除and之外的任何内容”(是的,即使是像这样的原语也可以分配给),并进行约束以使 no或类型可以潜入您的类型。undefinednullundefinednullundefinednullundefined{}nullundefinedstring{} T extends {}undefinednullT

重构将如下所示:

export interface UrlParam<T extends {}> {
  defaultValue: T | null | undefined,
  encode: (value: T) => string
  decode: (value: string) => T
}

然后UrlParam<LatLng | null>你可以写而不是UrlParam<LatLng>,最后以下encodeParam将按预期工作,因为编译器可以使用控制流分析来过滤一个类型的值,T | null | undefined只需T检查它:

function encodeParam<T extends {}>(urlParam: UrlParam<T>, value: T) {
  return value !== undefined && value !== null ? urlParam.encode(value) : null // okay
}

Playground 代码链接


推荐阅读