我们在渲染复杂对象,比如树组件的树对象,有嵌套对象/数组结构的时候,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} />;
});
}