首页 > 解决方案 > 使用来自两个相似接口的默认值的对象解构

问题描述

我有两个接口,UserBankUser. for的界面BankUser大致如下:

interface BankUser extends User {
  banks: { [bank_id: string]: string};
  isSuper: boolean;
}

我有一个函数,我要传入 aUser或 aBankUser并且我希望 aBankUser从中出来。如果是用户,则应为BankUser.

const cleanedUser = (user: User | BankUser): BankUser => {
  const {uid, displayName, email, phoneNumber, photoURL, banks = {}, isSuper = false} = user;
  return {uid, displayName, email, phoneNumber, photoURL, banks, isSuper} as BankUser;
}

我收到两个 TS2339 错误,表明 type 上不存在银行和 isSuper User | BankUser,例如:

TS2339:类型上不存在属性“银行”User | BankUser

如果我// @ts-ignore在函数上方添加,它可以工作,我可以发送 aUser或 a BankUser,但如果可能的话,我会尽量避免这种情况。有没有更好的方法来处理这个?

标签: typescript

解决方案


如果对象的类型中没有明确提及,TypeScript 不喜欢查找对象的属性。在这种情况下,User没有明确提及banksor isSuper,因此您会收到编译器错误。是的, aBankUser确实有这样的属性,但user可能是 a User,所以这是一个错误。

如果您想说服编译器查找该属性,则需要将其user视为比User | BankUser. 例如,您可以将其视为User & Partial<BankUser>. 已知该类型具有 的所有User属性,以及可选的来自 的额外属性BankUser。这给了你这个:

const cleanedUser = (user: User | BankUser): BankUser => {
    const {
        uid, displayName, email, phoneNumber, photoURL, banks = {}, isSuper = false
    } = user as User & Partial<BankUser>; // no error now
    return { uid, displayName, email, phoneNumber, photoURL, banks, isSuper };
}

另外,根据您的用例,我可能会cleanedUser这样实现:

const cleanedUser2 = (user: User | BankUser): BankUser => {
    return { banks: {}, isSuper: false, ...user };
}

这需要更少的类型操作和更少的打字。它的行为与cleanedUser假设传入的值user恰好aUser或 aBankUser并且没有额外的属性相同。


一旦您期望它user可能具有额外的属性,那么您可能需要对这两个cleanedUser()orcleanedUser2()的实现更加小心,因为 type 的值User可能具有 type 的额外isSuper属性number。这是完全有效的User,但是当您进行清洁时,您会看到奇怪的事情发生:

interface Hmm extends User {
    isSuper: number;
}
const hmm: Hmm = {
    uid: "", email: "", displayName: "", phoneNumber: "", photoURL: "", isSuper: 100
};
const beCareful = cleanedUser(hmm);
console.log(beCareful.isSuper) // boolean at compile time, number at runtime !!!

所以要小心。


好的,希望有帮助;祝你好运!

链接到代码


推荐阅读