Typescript中的对象多可能类型推导的解决办法

我们在渲染复杂对象,比如树组件的树对象,有嵌套对象/数组结构的时候,js的弱类型让我们一把梭,几乎没遇到什么问题(只是写的时候编译没问题把。233。。),结合TS后,对树状数据的递归访问会是一个问题:

比如数据源的数据声明为:

export interface VoiceSkillResponse {
  data: {
    isExistUpdate: number;
    isNewProduct: number;
    streamList: {
      streamId: string;
      streamName: string;
      intentList: {
        id: number;
        intent: string;
        isSelect: number;
      }[];
    }[];
  };
}

export interface VoiceSkill {
  streamId: string;
  streamName: string;
  intentList: Array<Intent>;
}

export interface Intent {
  id: number;
  intent: string;
  isSelect: number;
}

我们想根据这样的数据渲染一个树组件

  function renderNode(dataList: Array<VoiceSkill | Intent>) {
    dataList.map((v, k) => {
      if (v.intentList) {
        return (
          <Tree name={v.streamName} key={v.streamName}>
            {renderNode(v.intentList)}
          </Tree>
        );
      } 
      return <Tree name={v.intent} key={v.id} />;
    });
  }

| 的写法并不能让TS自动推导出v的类型和是否有访问属性,所以直接在编译层报错。

查阅TS文档:文档链接

我们可知至少两种解决方法:

as暴力

缺点,写很多as吧

  function renderNode(voiceSkillList: Array<VoiceSkill | Intent>) {
    voiceSkillList.map((v, k) => {
      if ((v as VoiceSkillList).intentList) {
        return (
          <Tree name={(v as VoiceSkillList).streamName} key={(v as VoiceSkillList).streamName}>
            {renderNode((v as VoiceSkillList).intentList)}
          </Tree>
        );
      }
      return <Tree name={(v as IntentList).intent} key={(v as IntentList).id} />;
    });
  }

写一个函数类型保护

个人认为目前最适合的方式?也是官方文档给的一种方式

  function isVoiceSkill(item: VoiceSkill | Intent): item is VoiceSkill {
    return (item as VoiceSkill).intentList !== undefined;
  }
  function renderNode(dataList: Array<VoiceSkill | Intent>) {
    dataList.map((v, k) => {
      if (isVoiceSkill(v)) {
        return (
          <Tree name={v.streamName} key={v.streamName}>
            {renderNode(v.intentList)}
          </Tree>
        );
      } else {
        return <Tree name={v.intent} key={v.id} />;
      }
    });
  }

函数重载

大佬说的一种,但是这样的话相当于参数在函数里还是any吧, 失去了类型检查...?重载主要是在调用的时候做类型检查用的,存疑。。。

  function renderNode(dataList: VoiceSkill[]): Array<React.ReactElement<any>>;
  function renderNode(dataList: Intent[]): Array<React.ReactElement<any>>;
  function renderNode(dataList: any[]): Array<React.ReactElement<any>> {
    return dataList.map((v, k) => {
      if (v.intentList) {
        return (
          <Tree name={v.streamName} key={v.streamName}>
            {renderNode(v.intentList)}
          </Tree>
        );
      }
      return <Tree name={v.intent} key={v.id} />;
    });
  }