TypeScript中无法对继承联合类型的泛型进行类型收窄
问题原因
TypeScript 目前无法将泛型的类型收窄结果和返回值中的条件类型(Extract/Exclude)做关联推导。你已经通过block.type === 'bulleted-list-item'明确了当前block的类型,但TS不会自动推导此时泛型B一定匹配Extract<B, BulletedListItemBlocklike>的约束,因此返回值会报类型不匹配的错误。
解决方案
方案1:使用函数重载(推荐,完全类型安全)
用重载声明对外暴露类型约束,内部直接用联合类型收窄即可,同时保证外部调用和内部逻辑的类型安全:
type ParagraphBlocklike = { type: 'paragraph'; }; type BulletedListItemBlocklike = { type: 'bulleted-list-item'; }; type Blocklike = | ParagraphBlocklike | BulletedListItemBlocklike ; type BulletedListRenderNode<B extends BulletedListItemBlocklike> = { type: 'bulleted-list'; items: B[] }; type RenderNode<B extends Blocklike> = | (Exclude<B, BulletedListItemBlocklike>) | BulletedListRenderNode<Extract<B, BulletedListItemBlocklike>>; // 重载签名,匹配不同入参的返回类型 function fnConvert<B extends ParagraphBlocklike>(block: B): B function fnConvert<B extends BulletedListItemBlocklike>(block: B): BulletedListRenderNode<B> function fnConvert(block: Blocklike): RenderNode<Blocklike> { if(block.type === 'bulleted-list-item') { return {type: 'bulleted-list', items: [block]}; } return block; }
方案2:最小粒度类型断言
如果需要保留原泛型写法,可以只对返回的对应值做断言,不会影响函数内部其他逻辑的类型安全:
const fnConvert = <B extends Blocklike>(block: B): RenderNode<B> => { if(block.type === 'bulleted-list-item') { // 此处断言完全符合类型收窄结果,无实际安全风险 return {type: 'bulleted-list', items: [block as Extract<B, BulletedListItemBlocklike>]}; } return block as Exclude<B, BulletedListItemBlocklike>; };
两种方案都可以正常使用,外部调用时都会正确返回对应的类型,不会丢失类型安全。
内容的提问来源于stack exchange,提问作者augzodia
相关产品推荐
相关产品推荐

