TypeScript中sort函数第二个参数报对象可能为undefined错误
问题现象
在Vue3 + TypeScript项目中使用数组sort方法编写排序比较逻辑时,TS对回调函数的第二个参数b抛出类型报错:Object is possibly 'undefined',但参数a与b属于完全相同的类型,a却未触发同类报错,现象不符合预期。
出错代码行:
stories.value.sort((a, b) => a.story_score? - b.story_score?)
完整业务代码上下文如下,报错位置位于getStoryWithAuthor方法内的数组排序逻辑处:
import { ref, Ref } from "vue" import useApi from "./useApi"; export interface Story { by: string descendants?: number id: number kids?: number[] score?: number time: number title?: string type: string url?: string } export interface Author { created: number id: string, karma: number, submitted: number[] } export interface StoryWithAuthor { author_id: string author_karma_score: number story_title?: string story_url ?: string timestamp: number story_score ?: number } const error: Ref<string>= ref('') const stories: Ref<StoryWithAuthor[]> = ref([]) const getStories = async () :Promise<void> => { error.value = '' stories.value = [] try { const { response: stories, request } = useApi<number[]>( 'topstories.json' ); await request() if (stories.value) { const storyIds: number[] = stories.value.sort(() => Math.random() - Math.random()).slice(0, 10) await getStoryWithAuthor(storyIds) } } catch (err: unknown) { error.value = 'Error fetching stories' } } const getStoryWithAuthor = async(storyIds: number[]): Promise<void> => { error.value = '' stories.value = [] try { for (const id of storyIds) { const { response: story, request } = useApi<Story>( `item/${id}.json` ); await request() if (story.value) { const author = await getAuthor(story.value.by) if (author) { stories.value.push( { author_id: author.id, author_karma_score: author.karma, story_title: story.value.title, story_url: story.value.url, timestamp: story.value.time, story_score: story.value.score, } ) } // Typescript complains about second argument stories.value.sort((a, b) => a.story_score? - b.story_score?) } } } catch (err: unknown) { error.value = 'Error fetching story' } } const getAuthor = async (userId: string):Promise<Author | undefined> => { error.value = '' try { const { response: author, request } = useApi<Author>( `user/${userId}.json` ); await request() return author.value } catch (err: unknown) { error.value = 'Error fetching author' } } type useStories = { getStories: () => Promise<void>, getStoryWithAuthor: (storyIds: number[]) => Promise<void>, stories: Ref<StoryWithAuthor[]>, error: Ref<unknown> } const useStories = () :useStories => ({ getStories, getStoryWithAuthor, stories, error, }) export default useStories
报错原因
出现仅参数b报错、a不报错的现象,核心是两个问题:
- 语法错误导致类型收窄逻辑异常:你写的
b.story_score?是错误语法,TypeScript会把属性名后的后置问号解析为三元运算符的起始标记,而非可选链操作符(访问嵌套可选属性的正确可选链写法是obj?.prop)。在这个解析逻辑下,a.story_score?被识别为三元的条件判断部分,TS自动对a的属性做了truthy类型收窄,因此不报错;而后面的-b.story_score?属于三元的真值分支部分,TS没有对这部分做自动类型收窄,就抛出了Object is possibly 'undefined'的错误。 - 排序逻辑位置不合理:你把
sort调用写在了for循环内部,每push一个新元素就对整个数组做全量排序,此时TS对迭代过程中的数组类型收窄本身就不完整,会放大类型校验的异常;同时这个写法本身性能很差,完全没有必要。
除此之外你当前写的排序逻辑本身也是无效的:sort的比较回调需要返回大于0、小于0、等于0的数值来决定排序顺序,你现在写的不完整三元表达式,在a.story_score为0(falsy值)时会直接返回undefined,最终排序结果完全不可预期。
修复方案
1. 调整排序逻辑位置
把sort调用移到for循环外面,等所有元素都push进数组之后再统一排序,既解决TS类型收窄不完整的问题,也能减少不必要的排序计算、提升性能。
2. 正确处理可选值的比较逻辑
因为story_score是可选属性,排序时要给undefined值设置默认兜底值(比如按0计算,可根据业务需求调整),返回合法的比较数值。
修复后的getStoryWithAuthor方法核心代码如下:
const getStoryWithAuthor = async(storyIds: number[]): Promise<void> => { error.value = '' stories.value = [] try { for (const id of storyIds) { const { response: story, request } = useApi<Story>( `item/${id}.json` ); await request() if (story.value) { const author = await getAuthor(story.value.by) if (author) { stories.value.push( { author_id: author.id, author_karma_score: author.karma, story_title: story.value.title, story_url: story.value.url, timestamp: story.value.time, story_score: story.value.score, } ) } } } // 所有元素插入完成后再统一排序 stories.value.sort((a, b) => { // 给可选score设置默认值0,按分数降序排列 const scoreA = a.story_score ?? 0 const scoreB = b.story_score ?? 0 return scoreB - scoreA }) } catch (err: unknown) { error.value = 'Error fetching story' } }
注意区分TS两种问号的使用场景:类型定义时属性名后加
?标记属性可选;访问属性时用?.做安全的可选链访问,不要把问号写在属性名后面当作可选标记,会被解析为三元运算符起始符导致语法和类型错误。
内容的提问来源于stack exchange,提问作者codeDragon
相关产品推荐
相关产品推荐

