
本文深入探讨了在typescript中如何根据函数参数动态返回不同类型的问题。从理解条件类型在泛型中的局限性出发,逐步介绍如何利用索引访问类型实现基于参数的条件返回,并提供了一种通过函数映射模式构建完全类型安全的解决方案,旨在帮助开发者编写更健壮、类型更明确的代码。
在TypeScript中,我们经常需要编写一个函数,它的返回类型取决于传入的参数。条件类型(Conditional Types)似乎是解决这一问题的理想工具。例如,考虑一个函数 createLabel,它根据输入是数字还是字符串返回不同接口类型:
interface IdLabel {
id: number;
// ... 其他字段
}
interface NameLabel {
name: string;
// ... 其他字段
}
type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel;
function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
if (typeof idOrName === 'number') {
return { id: idOrName }; // 错误:Type '{ id: number; }' is not assignable to type 'NameOrId<T>'
} else {
return { name: idOrName }; // 错误:Type '{ name: string; }' is not assignable to type 'NameOrId<T>'
}
}尽管逻辑上直观,但TypeScript编译器在此处会报错。这是因为当 T 是一个泛型类型参数时,编译器在函数体内部无法确定 T extends number 这个条件是真还是假。因此,NameOrId
这种限制是TypeScript设计中的一个已知问题,通常被称为“依赖类型函数(Dependent-Type-Like Functions)”的缺失,相关讨论可在 ms/TS#33014 中找到。
对于更常见的场景,例如一个根据操作名称返回不同结果的 fetch 或 fn 函数,我们可以结合泛型和索引访问类型来定义返回类型。
首先,定义不同操作可能返回的结果类型,并将它们映射到一个总类型中:
type GetResult = {
getData: string;
};
type PostResult = {
postData: string;
};
// 更多操作结果类型...
type ResultType = {
get: GetResult;
post: PostResult;
// ... 更多操作
};接着,我们可以定义 fn 函数,使其泛型参数 T 限制为 ResultType 的键,并使用 ResultType[T] 作为其返回类型:
function fn<T extends keyof ResultType>(operation: T): ResultType[T] {
if (operation === "get") {
// 这里的返回值类型是 GetResult,但编译器需要 ResultType[T]
// 因此需要进行类型断言
return { getData: "foo" } as ResultType[T];
} else {
// 这里的返回值类型是 PostResult
return { postData: "bar" } as ResultType[T];
}
}示例与效果:
语鲸
AI智能阅读辅助工具
314
查看详情
const res1 = fn("get");
// ^? 类型为 GetResult
const res2 = fn("post");
/
/ ^? 类型为 PostResult
// 传入不存在的操作键会报错
// const res3 = fn("put"); // 错误: Argument of type '"put"' is not assignable to parameter of type '"get" | "post"'.注意事项:
为了实现完全的类型安全,避免在函数体内部进行类型断言,我们可以采用一种更高级的函数映射模式。这种模式的核心思想是:先定义具体的实现,然后从这些实现中派生出类型定义。
type GetResult = {
getData: string;
};
type PostResult = {
postData: string;
};
// 1. 定义一个包含所有操作具体实现的内部对象
const _operations = {
get(): GetResult {
return { getData: "foo" };
},
post(): PostResult {
return { postData: "bar" };
},
// ... 更多操作函数
};
// 2. 从 _operations 派生出 ResultType
// ResultType 的每个键对应 _operations 中对应函数的返回值类型
type ResultType = {
[key in keyof typeof _operations]: ReturnType<(typeof _operations)[key]>;
};
// 3. 创建一个类型安全的 operations 对象,链接 _operations 和 ResultType
// 这一步确保 _operations 的结构与 ResultType 严格匹配
const operations: { [K in keyof ResultType]: () => ResultType[K] } = _operations;
// 4. 定义最终的 fn 函数,它直接调用类型安全的 operations 对象中的方法
function fn<T extends keyof ResultType>(operation: T): ResultType[T] {
return operations[operation]();
}原理分析:
优点:
在TypeScript中实现基于参数的条件返回类型是一个常见但充满挑战的需求。
理解这些模式不仅能帮助我们解决当前的问题,还能加深对TypeScript类型系统高级特性的理解,如 typeof、keyof 和 ReturnType 等,这些都是构建复杂且类型安全应用不可或缺的工具。随着TypeScript的不断发展,未来可能会有更直接的方式来支持“依赖类型函数”,但在此之前,函数映射模式提供了一个优雅且强大的解决方案。
以上就是TypeScript中实现基于参数的条件返回类型:深度解析与类型安全实践的详细内容,更多请关注其它相关文章!
# 一个函数
# BEING翻译网站建设
# 郑州网站推广258
# 抖音涉嫌推广营销
# 剧本杀营销推广
# 福田网站建设工程学校
# 郑州关键词搜索排名优化
# 新网站排名优化在线咨询
# 网站免费优化方法有哪些
# 梁山网站建设推广好不好
# 网页seo优化外包
# typescript
# 的是
# 服务端
# 只需
# 返回值
# 报错
# 体内
# 数据结构
# 我们可以
# 是一个
# 工具
相关文章:
Python中高效且防溢出的双曲正弦计算:基于对数空间的优化策略
QQ邮箱官方网站登录入口_QQ邮箱网页版在线使用
React/Next.js中实现列表项的动态移动与状态管理:兼论唯一键的重要性
12306怎么选座位选到安静区_12306选座安静区域选择策略
Django表单提交验证失败后保持字段值不刷新
sublime如何配置Go语言开发环境_sublime搭建Golang编译运行系统
MAC如何安全彻底地删除文件_MAC使用终端命令确保文件无法被恢复
12306选座怎么选到商务座_12306商务座选择与配置说明
J*aScript实现动态背景色下的文本与按钮颜色自适应调整
mcjs网页版流畅运行 mcjs低配电脑畅玩入口
J*aScript中向JSON对象添加新属性的正确姿势
解决J*aScript中重复选择项的确认对话框显示问题
css滚动区域卡顿如何改善_css滚动问题用will-change优化渲染
在python-socketio事件处理器中安全访问Flask应用上下文
Golang如何优雅处理error_Golang error处理最佳实践总结
火锅吃太多会怎样 火锅吃太多会上火吗
如何在更新Composer依赖后自动运行测试_使用post-update-cmd钩子触发PHPUnit
Kafka Streams中基于消息头条件过滤消息的实现指南
Composer如何解决json扩展缺失的错误
漫蛙2(台版)官方入口地址 漫蛙2(台版)正版漫画网页端
内存疯狂猛猛涨价:主板销量直接腰斩!
铁路12306官网网页端快速入口 铁路12306官方首页登录教程
J*aScript数据结构转换:将对象数组按类别分组
PHP中获取MongoDB服务器运行时间(Uptime)的专业指南
谷歌浏览器浏览体验优化_谷歌浏览器新版直连永久可用提示
将PCM16音频转换为W*并编码为Base64:浏览器环境下的手动处理指南
《主播少女的秘密账号迷宫》首支宣传片
Fabric Mod开发:在1.19.3+版本中正确添加自定义物品并管理物品组
b站如何看历史记录_b站观看历史找回方法
德邦快递查询平台 德邦快递物流信息查询入口
使用PHP从URL路径中提取倒数第二个片段
厨房不锈钢水槽发黑生锈怎么处理_水槽用可乐+锡纸2分钟抛亮如新
怎么搭建一个php网站源码_搭php网站源码搭建教程
构建轻量级网站内部消息系统:Formspree 集成指南
解决PHP会话Cookie在跨域请求中不保留的问题
qq邮箱发邮件给国外发不出去_QQ邮箱国际邮件发送失败原因与解决
c++ 获取系统当前时间 c++时间戳获取方法
Angular中父组件异步更新子组件复选框状态的实践指南
EMS快递官网app_中国邮政速递物流手机客户端
Win10系统服务哪些可以禁用 Win10安全优化服务列表【干货】
Composer的 "conflict" 字段有什么用_如何声明不兼容的包以避免依赖冲突
Fabric模组开发:自定义物品与物品组的现代管理方法
搜狗浏览器如何使用密码生成器创建强密码 搜狗浏览器内置密码安全工具
如何为你的Composer包编写自动化测试_集成PHPUnit到Composer的scripts工作流
AO3官方镜像站点汇总 AO3同人作品网页版直达链接
C++如何打印当前代码行号与文件名_C++预定义宏FILE与LINE的使用
在J*a里如何理解依赖关系的方向_依赖方向在模块结构中的作用
快手官方唯一登录入口 谨防山寨钓鱼网站
三星ZFold5多任务卡顿_Samsung ZFold5流畅度提升
Eclipse怎么运行工程_Eclipse工程运行配置说明
*请认真填写需求信息,我们会在24小时内与您取得联系。