Typescript高级: 深入理解extends keyof语法

概述

  • 在TypeScript中,extends关键字是类型系统中一个极其重要的组成部分
  • 它不仅用于类的继承,也是类型兼容性检查和泛型约束的关键机制
  • 特别是当它与keyof关键字结合,形成K extends keyof T的结构时
  • 它为类型系统带来了强大的灵活性和表达能力,让我们能够在泛型中对对象的属性进行操作和约束

K extends keyof T

  • 在TypeScript中,当你声明一个泛型约束为K extends keyof T
  • 这意味着泛型参数K被限制为只能是T类型上存在的属性名的子集
  • 这在处理对象属性、映射类型或者条件类型时非常有用

示例1

interface User {id: number;name: string;email: string;
}function getProperty<T, K extends keyof T>(user: T, key: K): T[K] {return user[key];
}const user = { id: 1, name: "Alice", email: "alice@example.com"};
console.log(getProperty(user, "name")); // 输出 "Alice"
  • 在这个例子中,K extends keyof User 确保了getProperty函数的key参数, 只能是User接口中定义的属性名

示例2 属性全面转化成只读

interface User {id: number;name: string;email: string;
}type ReadonlyStringFields<T> = {readonly [P in keyof T]: T[P];}type ReadonlyUser = ReadonlyStringFields<User>;// ReadonlyUser 类型为:
// {
//   id: number;
//   readonly name: string;
//   readonly email: string;
// }
  • 这里,ReadonlyStringFields 将所有属性转化为只读,当然

示例3:部分属性只读

type MakeSomePropertiesReadonly<T, K extends keyof T> = {readonly [P in K]: T[P];
} & {[P in Exclude<keyof T, K>]: T[P];
};interface UserInfo {id: number;username: string;email: string;isAdmin: boolean;
}type ReadonlyUserDetails = MakeSomePropertiesReadonly<UserInfo, 'id' | 'email'>;function displayUserInfo(user: ReadonlyUserDetails) {console.log(`ID: ${user.id}, Email: ${user.email}`);// 下面这行如果尝试在真实代码中执行,会因为类型检查而在编译时失败// user.id = 123; // Error: Cannot assign to 'id' because it is a read-only property.user.username = "NewUsername"; // 这是允许的,因为 username 不是只读的
}// 假设我们有一个UserInfo实例,为了演示,直接构造一个符合 ReadonlyUserDetails 的对象
const userDetails: ReadonlyUserDetails = {id: 42,username: "JohnDoe",email: "john.doe@example.com",isAdmin: false,
};displayUserInfo(userDetails);
  • 在 MakeSomePropertiesReadonly<T, K>
  • 泛型参数:
    • T: 表示你想要修改属性可读性的原始对象类型
    • K extends keyof T: 表示一个泛型约束,要求 K 必须是 T 类型的键(即属性名)的一个子集。这意味着你可以指定 T 中任意数量和名称的属性来变为只读
  • 类型别名结构:
    • { readonly [P in K]: T[P]; }: 这部分创建了一个新类型,其中 K 集合内的每个属性 P 被声明为只读。[P in K] 是一个映射类型,遍历 K 中的所有键,并为每个键创建一个属性,其值类型与 T[P] 相同,但加上了 readonly 修饰符。
    • & { [P in Exclude<keyof T, K>]: T[P]; } 这部分用来保留 T 类型中未被指定为只读的那些属性。Exclude<keyof T, K> 是一个实用类型,用于从 T 的所有键中排除已经在 K 中的键,确保剩余的属性不被重复定义且保持原样。

K extends keyof any

  • 当K extends keyof any时,这个约束实际上没有起到任何限制作用
  • 因为any类型在TypeScript中是最宽泛的类型,表示可以代表任何类型,所以任何类型都可以被认为是any的键
  • 这通常在你想要泛型参数可以是任何类型时使用,但这种用法在实践中较少见,因为失去了类型安全性的优势

示例

function getProperty<K extends keyof any>(obj: any, key: K): any {  return obj[key];  
}  const person = {  name: 'Alice',  age: 30,  address: '123 Main Street'  
};  // 由于使用了 keyof any,我们可以传入任何类型的键  
const name = getProperty(person, 'name'); // string  
const age = getProperty(person, 'age'); // number  
const address = getProperty(person, 'address'); // string  
const unknownProp = getProperty(person, 'unknownProp'); // undefined,但不会引发类型错误  console.log(name); // 输出: Alice  
console.log(age); // 输出: 30  
console.log(address); // 输出: 123 Main Street  
console.log(unknownProp); // 输出: undefined
  • keyof any表示任何可能的属性名,因为any类型可以包含任意属性
  • 使用K extends keyof any实际上对K没有太多限制,它可以是任意字符串或符号
  • 这种约束通常不是很有用,因为它不提供关于K具体可能是什么的明确信息

K extends keyof (string | number | symbol)

  • 这个表达式意味着泛型参数K可以是string或symbol类型中任何一个的键名
  • 在TypeScript中,对象的键通常是字符串或符号类型,但不包括数字(除非使用了计算属性名)
  • 因此,这个约束在直觉上可能用于处理特殊情况,比如当你知道泛型参数可能被用于索引一个映射到字符串或符号属性上,但实际上在标准对象操作中,数字作为键的用法不常见

示例

type AcceptableKeys = string | number | symbol;  function processKey<K extends AcceptableKeys>(key: K): void {console.log(`Processing key: ${key.toString()}`);  
}  // 使用字符串作为键  
processKey("myStringKey");  
// 使用数字作为键  
processKey(123);  // 使用符号作为键  
const mySymbol = Symbol("mySymbol");  
processKey(mySymbol);
  • 在这个示例中,我们定义了一个类型别名 AcceptableKeys,它表示可以接受的键类型是字符串、数字或符号
  • 然后,我们定义了一个泛型函数 processKey,它接受一个类型为 K 的参数,其中 K 被约束为必须扩展(extends)AcceptableKeys
  • 这样,我们就可以向 processKey 函数传递字符串、数字或符号类型的参数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/21179.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态SQL where, choose语句

where语句就一个<where>标签, 很简单, 不再过多赘述 接下来我们来看 choose语句的使用 其实choose语句就像java里的swith语句 , 如果语句前面的生效 , 后面的就不会生效了 可以定义查询的优先级

读人工智能时代与人类未来笔记19_读后总结与感想兼导读

1. 基本信息 人工智能时代与人类未来 (美)亨利基辛格,(美)埃里克施密特,(美)丹尼尔胡滕洛赫尔 著 中信出版社,2023年6月出版 1.1. 读薄率 书籍总字数145千字&#xff0c;笔记总字数39934字。 读薄率39934145000≈27.5% 1.2. 读厚方向 千脑智能 脑机穿越 未来呼啸而来 …

【工具】 MyBatis Plus的SQL拦截器自动翻译替换“?“符号为真实数值

【工具】 MyBatis Plus的SQL拦截器自动翻译替换"?"符号为真实数值 使用MyBatis的配置如下所示&#xff1a; mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl调用接口&#xff0c;sql日志打印如下&#xff1a; 参数和sql语句不…

Spring Boot配置MySQL数据库连接数

1.如何在Spring Boot中配置MySQL数据库的连接数 1.1主要配置 在Spring Boot中配置MySQL数据库连接数通常涉及到两个主要的配置&#xff1a; &#xff08;1&#xff09;数据源配置&#xff1a;这通常是在application.properties或application.yml文件中完成的&#xff0c;用于…

顶底背离的终极猜想和运用

这几天圈内都在传底蓓离什么的。作为严肃的量化自媒体&#xff0c;我们就不跟着吃这波瓜了。不过&#xff0c;我一直很关注技术指标的顶背离和底背离&#xff0c;一直在追问它的成因如何&#xff0c;以及如何预测。 底蓓离把我目光再次吸引到这个领域来&#xff0c;于是突然有…

Java如何实现二维数组行列转换

二维数组行列转换就是行号和列号互换 public class Erweishuzubianli {public static void main(String[] args) {int array[][]new int[][]{{8,75,23},{21,55,34},{15,23,20}};int temp;for(int i0;i<array.length;i){for(int j0;j<array[i].length;j){temparray[i][j]…

LitCTF 2024(公开赛道)——WP

目录 Misc 涐贪恋和伱、甾―⑺d毎兮毎秒 你说得对&#xff0c;但__ 盯帧珍珠 Everywhere We Go 关键&#xff0c;太关键了! 女装照流量 原铁&#xff0c;启动&#xff01; 舔到最后应有尽有 The love Web exx 一个....池子&#xff1f; SAS - Serializing Authent…

MySQL—函数—日期函数(基础)

一、引言 接下来讨论和学习关于函数的第三个方面——日期函数。 常见的MySQL当中的日期函数。 注意&#xff1a; 1、CURDATE()&#xff1a;cur&#xff1a;current 当前的&#xff0c;返回的是当前日期。 2、CURTIME()&#xff1a;当前时间。 3、NOW&#xff1a;当前的日期和…

Java语言高级编程:探索深层机制与应用技巧

Java语言高级编程&#xff1a;探索深层机制与应用技巧 在编程世界中&#xff0c;Java以其稳定、强大和跨平台的特性赢得了广泛的赞誉和应用。对于已经掌握Java基础知识的开发者来说&#xff0c;深入Java语言的高级编程领域&#xff0c;无疑将开启全新的技术视野。那么&#xf…

政安晨【零基础玩转各类开源AI项目】:解析开源项目的论文:Physical Non-inertial Poser (PNP)

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本文解析的原始论文为&#xff1a;https://arxiv.org/…

力扣1143. 最长公共子序列

给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08;也可以…

【TB作品】MSP430G2533,读取dht11,显示到lcd1602显示屏,串口发送到电脑

功能 读取dht11&#xff0c;显示到lcd1602显示屏&#xff0c;串口发送到电脑。 部分程序 void main(void) {char disp[20];char count 0;WDTCTL WDTPW WDTHOLD; // Stop WDTP1DIR 0Xff;P1SEL 0X00;P1SEL2 0X00;P2DIR 0Xff;P2SEL 0X00;P2SEL2 0X00;L…

为什么需要开局调用函数?

初始化操作&#xff1a;在你的应用程序启动时&#xff0c;可能需要执行一些初始化操作&#xff0c;例如设置默认值、加载配置、建立数据库连接等。开局调用函数可以帮助你集中管理这些操作&#xff0c;确保它们在应用程序启动时顺利执行。 统一入口&#xff1a;通过一个统一的…

打造你的专属Vue组件:基于FullCalendar超实用“日程任务管理组件”实战

打造你的专属Vue组件&#xff1a;基于FullCalendar超实用“日程任务管理组件”实战 在现代Web应用中&#xff0c;日程管理是一个常见而又关键的功能&#xff0c;它帮助用户高效安排和追踪日常任务及会议。Vue.js作为一个流行的前端框架&#xff0c;以其简洁的语法和强大的组件…

编译选项导致的结构体字节参数异常

文章目录 前言问题描述原因分析问题解决总结 前言 在构建编译工程时&#xff0c;会有一些对应的编译配置选项&#xff0c;不同的编译器&#xff0c;会有对应的配置项。本文介绍GHS工程中编译选项配置不对应导致的异常。 问题描述 在S32K3集成工程中&#xff0c;核1的INP_SWC…

transformer中的ffn

## import torch import torch.nn as nn import torch.nn.functional as F import logging logging.basicConfig(levellogging.INFO, format%(asctime)s %(levelname)s: %(message)s) # 定义FFN层 class FeedForwardNetwork(nn.Module): def __init__(self, input_dim, hi…

python运营商身份证二要素查验接口、身份证实名认证接口

随着网络服务安全需求的日益增长&#xff0c;个人信息的真实性和安全性成为了众多在线平台关注的焦点。近日&#xff0c;为应对这一挑战&#xff0c;翔云人工智能接口开放平台提供了Python语言的身份证二要素查验接口”及“实名认证接口”&#xff0c;旨在为各行业提供高效、准…

将字符串 “()“ ““ “|“ 条件组成的复杂表达式转换为ES查询语句

应用场景 "()" "&" "|" 这几个条件对于我们来说并不陌生, 其表达的逻辑非常明了, 又能通过很少的字符表达很复杂的嵌套关系, 在一些复杂的查询中会经常用到, 因此我最近也遇到了类似的问题,一开始觉得这类的工具应该挺常见的, 结果搜了半天…

JVM垃圾收集器和内存分配策略

概述 Java内存运行时数据区的程序计数器、虚拟机栈、本地方法栈3个区域会随着线程而产生&#xff0c;随线程而消失。这几个区域分配多少内存时在类结构确定下来即已知的&#xff0c;在这几个区域内就不需要过多考虑如何回收内存的问题&#xff0c;当方法结束或者线程结束时&am…

【spring】第一篇 IOC和DI入门案例

Spring到底是如何来实现IOC和DI的&#xff0c;那接下来就通过一些简单的入门案例&#xff0c;来演示下具体实现过程。 目录 前期准备 一、IOC入门案例 思路分析 代码实现 二、DI入门案例 思路分析 代码实现 总结 前期准备 使用IDEA创建Maven项目&#xff0c;首先需要配…