【问题标题】:TypeScript type-safety fails with JSON.parseTypeScript 类型安全因 JSON.parse 而失败
【发布时间】:2021-12-08 20:56:33
【问题描述】:

是我错了还是在解析 JSON 时 TypeScript 中的类型安全被抛弃了?

应该在这里遇到错误,但我没有:

interface Person {
  name: string
}

const person: Person = somePossibleFalsey ? JSON.parse(db.person) : undefined

上面的内容没有通过类型检查,当我认为它应该时。 db.person 变量可能不存在,这可能会将person 呈现为undefined。但是Person 不应该是undefined。据我所知,这是因为我使用的是JSON.parse

只是为了确认我应该得到一个错误,这是另一个 sn-p,它正确给了我一个错误:

const person: Person = Math.random() > .5 ? { name: 'Arthur' } : undefined

上面的代码会产生相应的 TypsScript 错误:

Type '{ name: string; } | undefined' is not assignable to type 'Person'.
  Type 'undefined' is not assignable to type 'Person'.ts(2322)

为什么JSON.parse 允许类型安全失败?或者这里还有其他什么在起作用?

【问题讨论】:

    标签: javascript json typescript type-safety


    【解决方案1】:

    我认为问题的核心是为什么允许将三元表达式的第二项undefined 作为分配给Person 的替代项之一。 This Github issue 将此描述为按预期工作。整个三元表达式得到第一个返回类型和第二个返回类型的联合,即any | undefined,它折叠为any,这反过来使它成为一个有效的赋值。 (any 是最弱的类型)。相反,如果将其包装在 if-else 块中,则会出现错误:

    let n: number;
    
    let x: any = {}
    n = x.z ? x.z : undefined // no error
    
    let y: any = {}
    if (y.z) {
        n = y.z
    } else {
        n = undefined // error
    }
    

    (source)

    我认为这里的不一致之处在于,在第一种情况下,整个三元表达式被推断为 any | undefined 可以折叠成任何东西;因此,编译器会看到一个单一的返回,any and all is right with the world(即使它不是)。在第二种情况下,有两个返回站点:一个是 return any 也可以,一个是 return undefined 不是。

    我认为挑战在于三元不是真正的语句而是表达式。表达式必须具有单一类型;在这种情况下,该类型恰好是 any | undefined 仅从任何性质来看都是不健全的。我怀疑,改变这一点将非常困难:当你在语句中间有一个三元组时会发生什么,例如作为函数的参数?当同一语句中有多个三元组时会发生什么[...]

    为了按照您建议的方式进行上述类型检查,编译器基本上必须复制该语句并单独对语句中每个三元组的真/假组合进行类型检查。你会有一个组合爆炸。

    我怀疑,目前的行为是我们将得到的最好结果。

    【讨论】:

    • 这其实是我最喜欢的解释。它承认这种奇怪的行为,并为其原因提供证据。我唯一的抱怨是它不包含代码。一个很好的例子是显示替代代码;即扩展的 if/else 语句等效于三元但产生错误...
    【解决方案2】:

    JSON.parse 返回一个any。并且由于any 包含任何类型(包括未定义),any | undefinedany 相同。

    使用as 输入JSON.parse 结果,您会得到预期的输出:

    const person: Person = db.person ? JSON.parse(db.person) as Person : undefined; 
    // Error: Type 'Person | undefined' is not assignable to type 'Person'. Type 'undefined' is not assignable to type 'Person'
    

    编辑:您似乎不清楚 any 类型。这基本上关闭了所有类型的安全性。每种类型都可以分配给anyany 可以分配给每种类型。当你这样做时

    const myAnyFunc = (): any => {return undefined;};
    const myPerson: Person = myAnyFunc();
    

    这不会产生 TypeError。 JSON.parse() 没有什么特别之处,只是任何返回 any 的“问题”。 查看this excellent TS book 了解更多关于any 的信息。

    【讨论】:

    • 但是该条件的第二部分明确地尝试分配undefined。为什么不抛出因为undefined 被分配给person 的可能性?请记住,db 等于 {},因此 db.person 不会评估为真值。
    • 我了解any,但db.person不是为真,因此内联条件落入undefined,而不是@987654347 @。为什么 TypeScript 允许这样做?
    • @banyudu 这是一个更好的答案。如果您将此放在正确的答案中,我将标记为正确。谢谢。
    • @shennan 类型转换是编译时行为,而不是运行时行为。所以除非 db.person 是静态的,否则 typescript 不知道它是真还是假。
    猜你喜欢
    • 2021-08-16
    • 2015-07-05
    • 2012-12-30
    • 2020-08-28
    • 2015-12-07
    • 1970-01-01
    • 1970-01-01
    • 2021-02-11
    • 2020-10-07
    相关资源
    最近更新 更多