网站搜索

TypeScript:如何优化一个可能有属性为 null 的类型


我正在尝试理解如何编写一个函数,该函数可以改进我的类型,然后返回它。

它对传入的对象本身以及函数体内部有效,但在外部无效。此外,我觉得奇怪的是,即使它在函数体内部有效,它在返回语句本身上也会失效。

我的期望是 TypeScript 能正确返回一个精炼/缩小的类型,检查的属性不仅包括主对象。

我哪里做错了?TypeScript 是否需要更多帮助,也许需要一些通用返回类型?

示例:

 
type Referrer = { id: string }

type User = {
    id: string;
    referrer: Referrer | null;
} | null;


function checkUserHasReferrer(user: User) {
  if (!user) {
      throw Error('No User');
  }

  if (!user.referrer) {
    console.log(user.referrer) // referrer correctly narrowed to `referrer: null`
    throw Error('No Referrer');
  }
  const referrer = user.referrer; // referrer correctly narrowed to `referrer: Referrer`
  console.log(user.referrer).     // same here, `referrer: Referrer`
  

  return user; // <- Here however it's back to `referrer: Referrer | null`
}


let initialUser: User = null;
const user = checkUserHasReferrer(initialUser);
console.log(user.id);           // <-- knows User is not `null`
console.log(user.referrer.id);  // However it doesn't know referrer is not `null`

TypeScript 游乐场

答案#1

你需要使用断言函数

type Referrer = { id: string }

type User = {
  id: string;
  referrer: Referrer | null;
}



function notNull(user: unknown): asserts  user {
  if (!user) {
    throw Error('No User');
  }
}

declare const user: User | null


notNull(user)
notNull(user.referrer)

console.log(user.id);
console.log(user.referrer.id);  // ok

操场

当你需要在 TypeScript 中抛出错误时,通常这样的函数是成为断言函数的好候选


或者,你可以使用自定义类型保护函数

type Referrer = { id: string }

type User = {
  id: string;
  referrer: Referrer | null;
}

const isValidUser = (user: User | null): user is User & { referrer: Referrer } =>
  Boolean(user && user.referrer)

declare const user: User

if (isValidUser(user)) {
  console.log(user.id);
  console.log(user.referrer.id);  // ok
}

操场

答案#2

这是 TypeScript 当前已知的一个限制。参见这里或这里。TypeScript 确实会正确地缩小属性的类型,但父对象的类型不会被修改,因此它仍然只是User。

在不使用类型断言的情况下,一个可能的解决方法是在返回对象时对其进行展开。

return {
    ...user,
    referrer
};

这会将函数的返回类型更改为

{
    referrer: Referrer;
    id: string;
}

操场

相关文章