TypeScript 类型安全失败 JSON.parse

TypeScript type-safety fails with JSON.parse

我是不是错了,或者在解析时类型安全在 TypeScript 中被抛出 JSON?

应该在这里得到一个错误,但我没有:

interface Person {
  name: string
}

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

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

只是为了确认我应该得到一个错误,这是另一个正确给我一个错误的片段:

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 允许类型安全失败?还是这里有其他事情在起作用?

JSON.parse returns 一个 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 类型。这实质上关闭了所有类型的安全。每个类型都可以分配给 any 并且 any 可以分配给每个类型。当你这样做时

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

这不会产生 TypeError。 JSON.parse() 没什么特别的,只是一个 'problem' 加上任何 returns any。 查看 this excellent TS book 以了解有关 any 的更多信息。

我认为问题的核心是为什么三元表达式的第二项 undefined 被允许作为分配给 Person 的替代项之一。 This Github issue 将此描述为按预期工作。整个三元表达式得到第一个 return 类型和第二个类型的并集,即 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)

I’m thinking the inconsistency here is that, in the first case, the entire ternary expression is inferred as any | undefined which collapses to just any; the compiler therefore sees a single return any and all is right with the world (even though it’s not). In the second case there are two return sites: one is return any which again is fine, and one is return undefined which is not.

I think the challenge is that a ternary isn't really a statement but an expression. An expression must have a single type; in this case that type happens to be any | undefined which is unsound simply by the nature of any. Changing this, I suspect, would be very difficult: what happens when you have a ternary in the middle of a statement, e.g. as an argument to a function? What happens when there are several ternaries in the same statement [...]

In order to type check the above the way you suggest, the compiler would basically have to duplicate the statement and individually type check it for every combination of true/false for every single ternary in the statement. You'd have a combinatorial explosion.

The current behavior is, I suspect, the best we're going to get.