使用早期返回来缩小类型范围以推断对象包含属性

问题描述 投票:0回答:1

我正在为一个对象编写一个类型保护。我使用早期返回来保持缩进的可读性,但这个例子是人为的。

我知道打字稿缩小仍然有很多漏洞,并且每个版本都会添加更多缩小逻辑。但是,仍然很难知道什么是用户错误以及 TypeScript 缩小仍然不支持什么。

我在下面有一个示例,我认为应该推断对象上存在属性,但也许我的逻辑不正确。

类型“{}”上不存在属性“a”。

有人可以澄清为什么在评论点没有推断出该属性吗?

type Example = {
  a: number
}

const guard = (example: unknown): example is Example => {
  if (
    example === undefined ||
    example === null ||
    typeof example === 'string' ||
    typeof example === 'number' ||
    Array.isArray(example)
  ) {
    return false
  } 
    
  if ((example as Example).a === undefined) {
    return false
  }

  // I expect narrowing to infer example as {a: unknown}
  // due to the if statement above

  // TS Error; Property 'a' does not exist on type '{}'.
  if (typeof example.a !== 'number') {
    return false
  }

  return true
}

const example: unknown = {
  a: 2
}

const result = guard(example)

我也尝试过

if (a.hasOwnProperty('a')) {
  return false
}

if (Object.hasOwn(example, 'a')) {
  return false
}
typescript narrowing type-narrowing
1个回答
0
投票

首先,这是编写此保护函数的一种复杂方法。 我会用这个代替:

type Example = {
  a: number
}

const guard = (example: unknown): example is Example => {
  if (example === null || typeof example !== 'object') {
    return false
  }
  
  return ('a' in example && typeof example.a === 'number');
}

const example: unknown = {
  a: 2
}

const result = guard(example);
console.log(result);

您使用了类型断言并对强制转换变量进行了未定义检查。这不会缩小原始变量

example

比较:

function chceckX1(x: unknown) {
  if (x === '123') {
    // x: '123'   
  }
}

function chceckX1(x: unknown) {
  if (x as string === '123') {
    // x: unknown, no narrowing on x   
  }
}

function chceckX2(x: unknown) {
  const z = x as string;
  if (z === '123') {
    // z: '123'   
  }
}

因此,您可以通过引入变量来修改代码

// Do not use, complex and confusing
const guard = (example: unknown): example is Example => {
  if (
    example === undefined ||
    example === null ||
    typeof example === 'string' ||
    typeof example === 'number' ||
    Array.isArray(example)
  ) {
    return false
  }
  const exampleCasted = example as Example 
  if (exampleCasted.a === undefined) {
    return false
  }

  if (typeof exampleCasted.a !== 'number') {
    return false
  }

  return true
}

请注意,虽然这可以正确检查输入对象的形状,但不会发生缩小 -

exampleCasted
Example
,这可能是不正确的。

TS 允许将数字与 null 进行比较。请参阅 禁止与 null 和 undefined 进行比较,除非它们在严格 null 模式下是有效的情况 #11920 此检查对

exampleCasted
类型没有影响。

© www.soinside.com 2019 - 2024. All rights reserved.