typescript 使用括号表示法访问嵌套的可选属性

bpsygsoo  于 2023-08-07  发布在  TypeScript
关注(0)|答案(2)|浏览(132)

我正在学习TypeScript。我为WhatsApp消息对象创建了我的类型,它根据消息类型发送一些属性:

// (edited and completed according to wonderflame comment)
type WhatsAppMessageObject = {
  id: string,
  from: string,
  type: string,
  audio?: {
    id?: string,
    mime_type?: string
  },
  document?: {
    caption?: string,
    filename: string,
    sha256: string,
    mime_type: string,
    id: string
  },
  text?: {
    body?: string
  },
  // More types. Cutted for brevity
}

type MessageData = {
  type: string,
  id?: string,
  mimeType?: string,
  optionId?: string;
  message?: string;
  normalizedMessage?: string;
}

字符串
因此,我确定了一些“文件”消息,即带有某种文件(文档、音频、图像等)的消息。当一些消息被发送到我的后端时,对象包括属性,即:

from: "someNumber",
  id: "someId",
  timestamp: "1690317478",
  type: "image",
  image: {
    mime_type: "image/jpeg",
    sha256: "someSha256",
    id: "someId",
  },
  text?: {
    body?: string
  },
  time
};


所以,我想要的是访问这个属性,如果是文件消息,文本消息或交互式消息,则执行不同的操作:

// Simplified for brevity (edited and completed according to wonderflame comment)

function handleMessageType(messageObject: WhatsAppMessageObject) {
  
  const {from, id: messageId} = messageObject
  const messageType = messageObject.type;
  const fileMessages: Array<keyof WhatsAppMessageObject> = ["audio", "document"];

  let messageData: MessageData = {
    type: messageType,
  };

  // If is a file message
  if (fileMessages.includes(messageType as keyof WhatsAppMessageObject)) {
    messageData.id = messageObject[messageType as keyof WhatsAppMessageObject]?.id;
    messageData.mimeType = messageObject[messageType as keyof WhatsAppMessageObject]?.mime_type;
    // TODO: Implement saving the files
  } 

  if(messageType === 'text') {
    messageData.message = messageObject.text!.body
    messageData.normalizedMessage = normalizeString(messageObject.text!.body)
  }

  return {messageId, from, messageData}
}

// Takes a string, make all letters lowercase and removes all special characters
function normalizeString(str = '') {
  let normalizedStr = str.toLowerCase()
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
  return normalizedStr
}


类型,如“作为WhatsAppMessageObject的键”是由chatGPT建议的,但我无法以任何方式解决“属性'id'不存在于类型'{ body?:string| undefined; }'“(handleMessageType函数片段中的第14行,第15行“mime_type”相同)。
拜托,我做错什么了?

cgvd09ve

cgvd09ve1#

让我们定义一个WhatsAppMessageObject的键数组,它们是FileMessage

// string[]
const fileMessages = [
  'audio',
  'document',
]

字符串
编译器将数组的类型扩展到基元。string[]不适合我们,为了防止扩展,让我们使用const assertion和satisfies操作符来确保类型检查:

// readonly ["audio", "document"]
const fileMessages = [
  'audio',
  'document',
] as const satisfies readonly  (keyof WhatsAppMessageObject)[];


现在,使用indexed accessFileMessage创建一个类型,它应该是fileMessages的元素的联合:

// "audio" | "document"
type FileMessage = typeof fileMessages[number];


由于fileMessage是一个数组/元组,它的键是number,通过使用number作为键,我们可以得到它所有值的并集。
我们还需要一个自定义的类型保护来确保传递的变量是FileMessage

const isFileMessage = (arg: unknown):arg is FileMessage => {
  return typeof arg === "string" && fileMessages.includes(arg as FileMessage)
}


因此,我们可以在你的函数中使用这个类型保护:

function handleMessageType(messageObject: WhatsAppMessageObject) {
  const { from, id: messageId } = messageObject;
  const messageType = messageObject.type;

  let messageData: MessageData = {
    type: messageType,
  };

  // If is a file message
  if (isFileMessage(messageType)) {
    messageData.id =
      messageObject[messageType]?.id;
    messageData.mimeType =
      messageObject[messageType]?.mime_type;
    // TODO: Implement saving the files
  }

  if (messageType === 'text') {
    messageData.message = messageObject.text!.body;
    messageData.normalizedMessage = normalizeString(messageObject.text!.body);
  }

  return { messageId, from, messageData };
}


Playground

tuwxkamq

tuwxkamq2#

我解决了虽然不是我想要的方式...无论如何,也许有人可以帮助我的解决方案有用,虽然不是最佳的。
所以,基本上是用“魔法字符串”填充函数(我不太喜欢,但无论如何,并不是说Meta将有数百种消息类型):

function handleMessageType(messageObject: WhatsAppMessageObject) {
  try {
    // extract the message ID, phone number and type of message from the payload
    const {from, type: messageType, id: messageId} = messageObject

    const otherMessages = ["button", "order"]; // TODO: This types of messages doesn't have an implementation yet

    let messageData: MessageData = { type: messageType };

    if (messageType === 'audio' || messageType === 'document' || messageType === 'image' || messageType === 'sticker' || messageType === 'video') {
      messageData.id = messageObject[messageType]!.id;
      messageData.mimeType = messageObject[messageType]!.mime_type;
      // TODO: Implement saving the files
    } else if(messageType === 'interactive') {
      const interactiveType = messageObject[messageType]!.type;
      messageData.optionId = messageObject[messageType]![interactiveType]!.id
    } else if (messageType === 'text') {
      messageData.message = messageObject.text!.body
      messageData.normalizedMessage = normalizeString(messageObject.text!.body)
    }

    return {messageId, from, messageData}
  } catch (err: any) { 
      err.name = 'messageTypeException'
      throw err
  }
}

字符串
请注意,我更改了“messageType”的“type”属性。这是因为“type”是一个保留关键字。
无论如何,我希望有人能提出一个更好、更可扩展的解决方案。

相关问题