TypeScript将对象从具有可选属性的类型转换为没有可选属性的类型

时间:2020-05-11 09:12:03

标签: typescript

我是TypeScript的新手,我面临一个非常琐碎的问题。我正在使用包装在我控制的服务功能中的第三方电子邮件库。第三方功能以及我的服务功能接受具有常见电子邮件配置属性(收件人,发件人,主题,正文等)的对象,但是第三方功能需要所有这些,而我的服务允许from属性是可选的。见下文。

// Third Party Code

type ThirdPartyEmailData = {
  from: string;
  to: string;
  subject: string;
  body: string;
}

const sendEmailWithThirdPartyService = (emailData: ThirdPartyEmailData) => {

  console.log('Sending email from:', emailData);

}

-----------------------------------------------------------------------------

// My Code

type MyEmailData = {
  from?: string;
  to: string;
  subject: string;
  body: string;
}

const sendEmail = (emailData: MyEmailData) => {

  emailData = { from: 'info@company.com', ...emailData };

  sendEmailWithThirdPartyService(emailData); // This does not compile

  /**
   * Argument of type 'MyEmailData' is not assignable to parameter of type 'ThirdPartyEmailData'.
   * Property 'from' is optional in type 'MyEmailData' but required in type 'ThirdPartyEmailData'.
   */

}

sendEmail({
  to: 'john.doe@customer.com',
  subject: 'I <3 TypeScript',
  body: 'But TypeScript hates me'
});

按预期,由于无法将类型“ MyEmailData”分配给类型“ ThirdPartyEmailData”的参数,因此上述内容无法编译。我一直在寻找一种干净的方法来转换/更改/广播emailData的类型,然后再将其传递给第三方函数,但是我真的找不到一个好方法。以下是我想到的一些内容,但感觉不太好。

解决方案1:只需转换为第三方类型

// My Code

const sendEmail = (emailData: MyEmailData) => {

  // If you remove the line below the program still compiles but breaks at runtime
  emailData = { from: 'info@company.com', ...emailData };

  sendEmailWithThirdPartyService(<ThirdPartyEmailData>emailData); // This compiles

}

解决方案2:使用类型保护以确保存在from属性

// My Code

const sendEmail = (emailData: MyEmailData) => {

  emailData = { from: 'info@company.com', ...emailData };

  // By using the type guard, we ensure that the type of emailData overlaps ThirdPartyEmailData
  if (! hasFromProperty(emailData)) throw new Error('From property is missing');

  sendEmailWithThirdPartyService(emailData); // This compiles

}

-----------------------------------------------------------------------------

// Utilities

/** Utility to make certain keys of a type required */
type RequiredKeys<T, K extends keyof T> = Exclude<T, K> & Required<Pick<T, K>>

/** Typeguard for property 'from' in MyEmailData */
const hasFromProperty = (data: MyEmailData): data is RequiredKeys<MyEmailData, 'from'> => {

  return 'from' in data;

}

这似乎是一个非常普遍的问题,但我一直未能找到令人满意的解决方案。您能推荐什么?

1 个答案:

答案 0 :(得分:1)

您应避免更改参数。就您而言,解决方案很简单:

const sendEmail = (emailData: MyEmailData) => {
  const thirdPartyEmailData: ThirdPartyEmailData  = { from: 'info@company.com', ...emailData };
  sendEmailWithThirdPartyService(thirdPartyEmailData);
}

即使在不声明被推断出的ThirdPartyEmailData const thirdPartyEmailData = { from: 'info@company.com', ...emailData };类型的情况下,它也可以正常工作。