为何TypeScript泛型在此场景下无法正确推断setSortableFields类型并自动补全?
为什么TypeScript无法在此场景下正确推断setSortableFields的类型?
先看代码示例:
export type TesteProps< SortableFields extends true | false > = ( { pending: boolean, sortedFields: object, sortableFields: SortableFields } ) & ( SortableFields extends true ? { setSortedFields: object } : { setSortedFields?: never } ); export function Teste< SortableFields extends true | false >( props: TesteProps<SortableFields> ) { if (props.sortableFields) { // 此处TypeScript自动推断类型为 Object | undefined,而非预期的Object const setSortableFields = props.setSortedFields; console.log(setSortableFields); } return <>{JSON.stringify(props)}</>; }
问题原因
TypeScript的控制流分析无法将运行时的props.sortableFields判断与泛型参数SortableFields的条件类型分支建立关联。
泛型参数SortableFields是在函数调用时确定的,函数内部的if (props.sortableFields)只是对具体属性值的判断,TypeScript不会据此反向推导泛型参数的具体类型——它没办法确定此时SortableFields必然是true,所以只能保留setSortedFields的联合类型object | undefined。
解决方法
有几种可行的方式让TypeScript正确推断类型:
- 使用类型断言
直接在赋值时明确断言类型:
if (props.sortableFields) { const setSortableFields = props.setSortedFields as object; console.log(setSortableFields); }
- 自定义类型守卫
通过类型守卫函数告诉TypeScript,当props.sortableFields为true时,props的类型对应SortableFields=true的分支:
function isSortable<T extends true | false>(props: TesteProps<T>): props is TesteProps<true> { return props.sortableFields === true; } export function Teste<SortableFields extends true | false>(props: TesteProps<SortableFields>) { if (isSortable(props)) { const setSortableFields = props.setSortedFields; // 此时类型推断为object console.log(setSortableFields); } return <>{JSON.stringify(props)}</>; }
- 重构类型为可辨识联合
把TesteProps改成可辨识联合类型,让TypeScript能通过sortableFields直接收窄整体类型:
export type TesteProps = { pending: boolean, sortedFields: object, sortableFields: true, setSortedFields: object } | { pending: boolean, sortedFields: object, sortableFields: false, setSortedFields?: never }; export function Teste(props: TesteProps) { if (props.sortableFields) { const setSortableFields = props.setSortedFields; // 类型推断为object console.log(setSortableFields); } return <>{JSON.stringify(props)}</>; }
内容的提问来源于stack exchange,提问作者Cristofer
相关产品推荐
相关产品推荐

