为何TypeScript类型收窄在回调函数中无法生效?
TypeScript类型收窄在回调函数中失效的问题
引发错误的代码
function upload( url: string, files: { file: File; fieldname: string; }[], options: { onProgress: Function; customData?: Record<string, any>; method?: string; } = { onProgress() {}, method: "POST", } ) { options.customData && Object.keys(options.customData).forEach( (fieldname) => typeof options.customData[fieldname] !== "undefined" && formData.append(fieldname, options.customData[fieldname]) ); }
遇到的错误
'options.customData' is possibly 'undefined'. ts(18048)
按照TypeScript的类型收窄规则,options.customData && 本应将options.customData的类型从Record<string, any> | undefined收窄为Record<string, any>,但在forEach的回调函数内部访问时,该类型收窄不生效。
类型收窄有效的对比代码
function upload( url: string, files: { file: File; fieldname: string; }[], options: { onProgress: Function; customData?: Record<string, any>; method?: string; } = { onProgress() {}, method: "POST", } ) { options.customData && options.customData["fieldname"]; }
原因分析
TypeScript的类型收窄作用域仅限于当前同步代码块。对于嵌套的回调函数,TypeScript无法保证回调执行时,options.customData的值没有被修改(哪怕变量是函数内的局部变量,回调的执行时机脱离了原收窄逻辑的同步上下文),因此不会保留外部代码块的类型收窄状态。
解决方案
1. 赋值给局部变量
将options.customData赋值给局部变量,TypeScript能跟踪该变量的类型收窄状态(局部变量不会被外部修改):
function upload( url: string, files: { file: File; fieldname: string; }[], options: { onProgress: Function; customData?: Record<string, any>; method?: string; } = { onProgress() {}, method: "POST", } ) { const customData = options.customData; customData && Object.keys(customData).forEach( (fieldname) => typeof customData[fieldname] !== "undefined" && formData.append(fieldname, customData[fieldname]) ); }
2. 使用非空断言(谨慎使用)
如果你能100%确认回调执行时options.customData不会为undefined,可以用非空断言!跳过类型检查:
function upload( url: string, files: { file: File; fieldname: string; }[], options: { onProgress: Function; customData?: Record<string, any>; method?: string; } = { onProgress() {}, method: "POST", } ) { options.customData && Object.keys(options.customData).forEach( (fieldname) => typeof options.customData![fieldname] !== "undefined" && formData.append(fieldname, options.customData![fieldname]) ); }
注意:非空断言会绕过TypeScript检查,若实际运行时变量变为undefined,会触发运行时错误,仅在确定变量状态时使用。
3. 自定义类型守卫
通过自定义类型守卫明确收窄类型,逻辑更严谨:
function isDefined<T>(value: T | undefined): value is T { return typeof value !== "undefined"; } function upload( url: string, files: { file: File; fieldname: string; }[], options: { onProgress: Function; customData?: Record<string, any>; method?: string; } = { onProgress() {}, method: "POST", } ) { if (isDefined(options.customData)) { Object.keys(options.customData).forEach( (fieldname) => typeof options.customData[fieldname] !== "undefined" && formData.append(fieldname, options.customData[fieldname]) ); } }
内容的提问来源于stack exchange,提问作者hoho hotstuff
相关产品推荐
相关产品推荐

