为什么 Typescript 会尝试将条件类型的一个分支与另一个匹配?

Why Typescript tries to match one branch of conditional type with another?

我有一个小的实用函数,现在我正在尝试使用泛型为其编写更具体的类型。

代码如下:

/* global localStorage */

const ls = localStorage

export type ParsedJSON<T> = T | null
export type Falsy = false | undefined

function get<T = any> (
  key: string,
  parseJSON?: boolean
): typeof parseJSON extends Falsy ? string : ParsedJSON<T> {
  const value = ls.getItem(key) ?? ''

  if (parseJSON === true) {
    let result: ParsedJSON<T> = null

    try {
      result = JSON.parse(value)
    } catch (err) {}

    return result
  } else if ((parseJSON === false)) {
    return value
  }

  return null
}

else 分支出现错误:Type 'string' is not assignable to type 'ParsedJSON<T>'

else 分支在这里有点多余,但我明确添加了它以调查此错误)。

这里没看懂,为什么TS会尝试匹配string类型和ParsedJSON<T>类型

对我来说唯一合理的解释是false不能分配给Falsy类型,所以return类型被计算为ParsedJSON<T>。但我已经检查过情况并非如此:

所以,请帮助我了解这里发生了什么。 ) 谢谢。

看起来发生的事情是 TS 评估类型表达式 typeof parseJSON extends Falsy ? string : ParsedJSON<T> 不是作为调用函数时要使用通用参数评估的条件表达式,而是立即基于有关的当前可用信息函数。

有了这些知识,我们就可以确定编译器推断出的 return 类型是什么。表达式为

typeof parseJSON extends Falsy ? string : ParsedJSON<T>

parseJSON 在评估时的类型是 boolean | undefined,因此它 而不是 扩展 Falsyfalse | undefined,因此类型更窄。

因此get的return类型确定为:ParsedJSON<T>.

我能想到的基于调用站点实际参数值的条件 return 类型的唯一方法是,正如@Nishant 建议的那样,函数重载。例如:

function get<T = any>(key: string, parseJSON: true): ParsedJSON<T>
function get<T = any>(key: string, parseJSON?: false): string
function get<T = any>(key: string, parseJSON?: boolean) {
  const value = ls.getItem(key) ?? ''

  if (parseJSON === true) {
    let result: ParsedJSON<T> = null

    try {
      result = JSON.parse(value)
    } catch (err) { }

    return result
  } else if ((parseJSON === false)) {
    return value
  }

  return null
}

现在函数将根据 parseJSON 的类型具有正确的 return 类型。

get<number>('', true) // => ParsedJSON<number>
get<number>('', false) // => string
get<number>('') // => string

See this code in the TS playground