TypeScript如何定义第二个参数类型依赖首个参数值的函数类型
问题根因
TypeScript 不会自动为两个独立声明的联合类型参数建立关联约束:即使在 switch 分支中收窄了fileType的字面量类型,TS 仍会判定fileData为TFileType1 | TFileType2的联合类型,无法自动匹配到当前分支对应的具体类型,因此调用转换函数时抛出类型不匹配错误。
解决方案
通过映射类型+泛型约束建立两个参数的类型关联,即可让TS在分支判断时自动收窄类型,同时保证后续赋值逻辑的类型安全,后续扩展文件类型时也只需要修改映射配置,维护成本更低。
第一步:定义类型映射关系
先建立文件类型与原始内容、转换结果的对应关系,替代零散的联合类型声明:
// 文件类型 -> 原始内容结构 映射 type FileTypeMap = { type1: TFileType1 type2: TFileType2 } // 文件类型 -> 转换后结构 映射 type TransformResultMap = { type1: TFile1Transform type2: TFile2Transform }
原有基础类型可以直接从映射派生,不需要重复声明:
type TFileTypes = keyof FileTypeMap // 等价于 'type1' | 'type2' type TFileContentType = FileTypeMap[TFileTypes] // 等价于 TFileType1 | TFileType2
第二步:改造parseFileData为泛型函数
通过泛型约束入参fileType和fileData的类型必须一一对应,同时明确返回值和入参类型的关联:
const parseFileData = <T extends TFileTypes>( fileType: T, fileData: FileTypeMap[T] ): TransformResultMap[T] => { switch(fileType) { case 'type1': { // 此分支下TS会自动收窄fileData为TFileType1,无类型错误 return transformFile1Data(fileData) as TransformResultMap[T] } case 'type2': { // 此分支下TS会自动收窄fileData为TFileType2,无类型错误 return transformFile2Data(fileData) as TransformResultMap[T] } default: throw new Error(`不支持的文件类型: ${fileType}`) } }
此处的
as类型断言是安全的:分支判断已经保证了返回值和泛型T的类型完全匹配。
第三步:修正fileData数组类型
给fileData添加可辨识联合类型标注,保证遍历时fileType和content的类型关联,让loadFileData中的赋值逻辑也能通过类型校验:
// 定义文件数据项的可辨识联合类型 type FileDataItem = { [K in TFileTypes]: { fileType: K content: FileTypeMap[K] } }[TFileTypes] // 给数组添加类型标注 const fileData: FileDataItem[] = [ { fileType: 'type1', content: { name: 'fname', val1: 'val1' } }, { fileType: 'type2', content: { name: 'fname2', val2: 'val2' } } ]
完整可运行代码
type TFileType1 = { name: string val1: string } type TFileType2 = { name: string val2: string } type TFile1Transform = { val1t: string, val2t: string } type TFile2Transform = { val11t: string val22t: string } type TProjectData = { 'type1'?: TFile1Transform, 'type2'?: TFile2Transform } // 类型映射定义 type FileTypeMap = { type1: TFileType1 type2: TFileType2 } type TransformResultMap = { type1: TFile1Transform type2: TFile2Transform } type TFileTypes = keyof FileTypeMap type TFileContentType = FileTypeMap[TFileTypes] type FileDataItem = { [K in TFileTypes]: { fileType: K content: FileTypeMap[K] } }[TFileTypes] const fileData: FileDataItem[] = [ { fileType: 'type1', content: { name: 'fname', val1: 'val1' } }, { fileType: 'type2', content: { name: 'fname2', val2: 'val2' } } ] const loadFileData = () => { const projectData: TProjectData = {} for (const data of fileData) { const fileType = data.fileType const resultData = parseFileData(fileType, data.content) projectData[fileType] = resultData } return projectData } const transformFile1Data = (fileData: TFileType1): TFile1Transform => { return { val1t: fileData.name, val2t: fileData.val1 } } const transformFile2Data = (fileData: TFileType2): TFile2Transform => { return { val11t: fileData.name, val22t: fileData.val2 } } const parseFileData = <T extends TFileTypes>( fileType: T, fileData: FileTypeMap[T] ): TransformResultMap[T] => { switch(fileType) { case 'type1': { return transformFile1Data(fileData) as TransformResultMap[T] } case 'type2': { return transformFile2Data(fileData) as TransformResultMap[T] } default: throw new Error(`不支持的文件类型: ${fileType}`) } }
内容的提问来源于stack exchange,提问作者Adharsh M
相关产品推荐
相关产品推荐

