【问题标题】:Typescript type fails if object is destructed如果对象被破坏,打字稿类型会失败
【发布时间】:2019-12-11 02:41:28
【问题描述】:

我有一个函数返回带有两个道具(res,mes)的对象,其中一个为空:

const fetchJSON = <Res, Body>(link: string, body: Body): Promise<{ res: Res; mes: null } | { res: null; mes: Popup }> => {
  return new Promise(resolve => {
    fetch(link,
      body: JSON.stringify(body)
    })
      .then(res => res.json())
      .then((resJson: Res) => {
        resolve({ res: resJson, mes: null })
      })
      .catch(err => {
        resolve({ res: null, mes: { type: 'err', text: 'some error' } })
      })
  })
}

如果在我使用 fetch 的响应而不进行破坏之后一切正常:

 const result = await fetchJSON<ResReaderData, ApiReaderData>('api/reader_data', { id })
 if (result.mes) return popupPush(result.mes)
 setProfile(result.res.reader)

但是如果我使用对象分解:

const { res, mes } = await fetchJSON<ResReaderData, ApiReaderData>('api/reader_data', { readerId, role: 'alfa' })
  if (mes) return popupPush(mes)

  console.log(res.id)

即使我检查了 mes,Typescript 也不明白 res 不为空:

有没有办法解决这个问题,或者我只需要忘记对象破坏?

或者也许还有其他方法可以用于这样的包装器?

【问题讨论】:

标签: typescript


【解决方案1】:

如果您知道您正在处理的属性不是null。您可以使用non-null assertion operator (!) 断言它不是nullundefined

例如console.log(res!.id):

function getResult(type: 'res' | 'mes') {
  let result: {
    res?: { foo: string },
    mes?: { bar: string },
  } = {};

  if (type === 'res') {
    result.res = {
      foo: 'res',
    };
  } else {
    result.mes = {
      bar: 'mes',
    };
  }

  return result;
}

const { res, mes } = getResult('res');

console.log(res!.foo); // non-null assertion
console.log(mes?.bar); // optional chaining

此外,如果前面的属性是null,您可以使用optional chaining operator (?) 让表达式返回null

【讨论】:

    【解决方案2】:

    据我所知,这是当前打字稿的限制。在你解构的时候,类型是{ res: Res; mes: null } | { res: null; mes: Popup },所以在解压时必须考虑所有的可能性,这意味着resmessages都可以为空。

    将两个变量分开后,联合之前无法链接这两个变量,它们是分开的。我期待有朝一日可以克服这一限制,但到目前为止,您仍被困在将守卫保持在一个对象内。

    如果您的数据结构允许,一个可能的解决方案是只返回一个可以区分的字段:

    const fetchJSON = <Res, Body>(link: string, body: Body): Promise<{ res: Res; mes: null } | { res: null; mes: Popup }> => {
      return new Promise(resolve => {
        fetch(link,
          body: JSON.stringify(body)
        })
          .then(res => res.json())
          .then((resJson: Res) => {
            resolve(resJson)
          })
          .catch(err => {
            resolve({ type: 'err', text: 'some error' })
          })
      })
    }
    
    function isErrorMessage(obj: any): obj is Popup {
        return obj && obj.type === "err";
    }
    
    async function MAIN(){
    
        const result = await fetchJSON<ResReaderData, ApiReaderData>('api/reader_data', { id })
         if (isErrorMessage(result)) return popupPush(result)
         setProfile(result.reader)
    }
    

    【讨论】:

      猜你喜欢
      • 2021-11-05
      • 2019-02-06
      • 2019-07-28
      • 2016-08-03
      • 1970-01-01
      • 1970-01-01
      • 2018-09-15
      • 2020-06-10
      • 1970-01-01
      相关资源
      最近更新 更多