Swift中如何向数组添加泛型项?解决类型不匹配报错
解决Swift中泛型闭包存入数组的类型不匹配问题
问题分析
你遇到的核心问题是:泛型函数get中的completion是针对具体T: Decodable类型的闭包(Result<T, ApiError>),但数组messages定义的completion类型是Result<any Decodable, ApiError>,两者无法直接转换——因为Result<T, ...>和Result<any Decodable, ...>属于不同的类型,Swift不允许直接隐式转换。
解决方案:用类型擦除包装类统一类型
要存储不同泛型类型的闭包,我们需要用类型擦除的方式,把泛型闭包封装成非泛型的可存储类型:
步骤1:定义类型擦除包装类
这个类会把具体泛型类型的闭包转换成统一的非泛型接口:
// 先确保你的ApiError包含类型不匹配的错误 case(如果没有可以添加) enum ApiError: Error { case typeMismatch // 其他已有的错误 case... } class AnyCompletionWrapper { private let handleClosure: (Result<any Decodable, ApiError>) -> Void // 初始化时接收具体泛型类型的completion init<T: Decodable>(_ completion: @escaping (Result<T, ApiError>) -> Void) { self.handleClosure = { result in switch result { case .success(let decodedValue as T): // 类型匹配,调用原completion返回正确类型 completion(.success(decodedValue)) case .failure(let error): // 直接传递错误 completion(.failure(error)) case .success: // 类型不匹配时返回对应的错误 completion(.failure(.typeMismatch)) } } } // 对外暴露调用闭包的方法 func handle(_ result: Result<any Decodable, ApiError>) { handleClosure(result) } }
步骤2:修改数组类型
把原来的数组改成存储AnyCompletionWrapper类型的completion:
var messages: [(url: URL, completion: AnyCompletionWrapper)] = []
步骤3:在get函数中存入数组
现在可以正常append泛型闭包了,因为我们把它包装成了AnyCompletionWrapper:
func get<T: Decodable>(from url: URL, type: T.Type, completion: @escaping (Result<T, ApiError>) -> ()) { messages.append((url, AnyCompletionWrapper(completion))) }
步骤4:测试时调用闭包
当你需要模拟API返回、触发completion时,只需取出数组中的AnyCompletionWrapper,调用handle方法即可:
// 示例:模拟返回TimetableModel let mockTimetable = TimetableModel(/* 初始化模拟数据 */) if let message = messages.first(where: { $0.url == targetUrl }) { message.completion.handle(.success(mockTimetable)) }
补充说明
这种方式既保留了泛型闭包的类型安全性,又能统一存储不同泛型类型的闭包,完全适配你的测试场景——比如原函数getTimeTable调用httpClient.get时,传入的TimetableModel类型的completion会被正确包装存入数组,后续模拟返回时能准确触发对应的回调。
内容的提问来源于stack exchange,提问作者mars_dev
相关产品推荐
相关产品推荐

