使用JSX语法调用组件时TypeScript类型收窄失效问题求助
JSX调用自定义组件时TypeScript类型收窄失效的解决办法
问题本质
直接以函数形式调用ShowType时,TypeScript能通过上下文联动推断isB的返回类型,自动收窄asB的参数类型;但用JSX语法调用时,TypeScript对JSX属性的泛型推断是独立进行的,无法关联isB和asB的类型依赖,导致asB的参数无法被正确识别为signedIn类型的User。
可行解决方案
1. 显式指定泛型参数
在JSX标签上手动声明ShowType的泛型参数,明确类型约束:
const Greeting2 = (s: User) => ( <ShowType<User, { tag: "signedIn"; userName: string; avatar: string }> value={s} isB={(i) => (i.tag === "signedIn" ? i : undefined)} asB={(b) => <div>Hi, {b.userName}</div>} fallback={<div>Hello, stranger!</div>} </ShowType> );
2. 优化组件类型定义(推荐)
把isB改成类型守卫函数,让TypeScript能通过类型断言逻辑自动关联asB的参数类型,适配JSX的推断机制:
type JSXElement = JSX.Element; function ShowType<A, B extends A>(props: { value: A; isB: (a: A) => a is B; asB: (i: B) => JSXElement; fallback: JSXElement; }) { return props.isB(props.value) ? props.asB(props.value) : props.fallback; } type User = | { tag: "anonymous" } | { tag: "signedIn"; userName: string; avatar: string }; // 函数式调用不受影响 const Greeting1 = (s: User) => ShowType({ value: s, isB: (i): i is Extract<User, { tag: "signedIn" }> => i.tag === "signedIn", asB: (b) => <div>Hi, {b.userName}</div>, fallback: <div>Hello, stranger!</div>, }); // JSX调用现在能正确推断类型 const Greeting2 = (s: User) => ( <ShowType value={s} isB={(i): i is Extract<User, { tag: "signedIn" }> => i.tag === "signedIn"} asB={(b) => <div>Hi, {b.userName}</div>} fallback={<div>Hello, stranger!</div>} /> );
这种方式既保留了严格的类型安全,又让JSX和函数式调用的类型推断都能正常工作,是最优雅的解决方案。
3. 临时用类型断言(不推荐)
如果不想修改组件定义,可在asB参数里手动添加类型断言,但会丢失部分类型安全:
const Greeting2 = (s: User) => ( <ShowType value={s} isB={(i) => (i.tag === "signedIn" ? i : undefined)} asB={(b) => <div>Hi, {(b as Extract<User, { tag: "signedIn" }>).userName}</div>} fallback={<div>Hello, stranger!</div>} /> );
总结
优先选择方案2,通过类型守卫优化组件类型,兼顾类型安全和开发体验;方案1需要手动重复类型,不够简洁;方案3仅适合临时应急场景。
内容的提问来源于stack exchange,提问作者JustinM
相关产品推荐
相关产品推荐

