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

2024-06-03 02:04

本文主要是介绍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 函数传递字符串、数字或符号类型的参数

这篇关于Typescript高级: 深入理解extends keyof语法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1025726

相关文章

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

从基础到高级详解Python数值格式化输出的完全指南

《从基础到高级详解Python数值格式化输出的完全指南》在数据分析、金融计算和科学报告领域,数值格式化是提升可读性和专业性的关键技术,本文将深入解析Python中数值格式化输出的相关方法,感兴趣的小伙... 目录引言:数值格式化的核心价值一、基础格式化方法1.1 三种核心格式化方式对比1.2 基础格式化示例

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

深度解析Python yfinance的核心功能和高级用法

《深度解析Pythonyfinance的核心功能和高级用法》yfinance是一个功能强大且易于使用的Python库,用于从YahooFinance获取金融数据,本教程将深入探讨yfinance的核... 目录yfinance 深度解析教程 (python)1. 简介与安装1.1 什么是 yfinance?

MySQL数据类型与表操作全指南( 从基础到高级实践)

《MySQL数据类型与表操作全指南(从基础到高级实践)》本文详解MySQL数据类型分类(数值、日期/时间、字符串)及表操作(创建、修改、维护),涵盖优化技巧如数据类型选择、备份、分区,强调规范设计与... 目录mysql数据类型详解数值类型日期时间类型字符串类型表操作全解析创建表修改表结构添加列修改列删除列

深入理解go中interface机制

《深入理解go中interface机制》本文主要介绍了深入理解go中interface机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前言interface使用类型判断总结前言go的interface是一组method的集合,不

Python 函数详解:从基础语法到高级使用技巧

《Python函数详解:从基础语法到高级使用技巧》本文基于实例代码,全面讲解Python函数的定义、参数传递、变量作用域及类型标注等知识点,帮助初学者快速掌握函数的使用技巧,感兴趣的朋友跟随小编一起... 目录一、函数的基本概念与作用二、函数的定义与调用1. 无参函数2. 带参函数3. 带返回值的函数4.

深入解析Java NIO在高并发场景下的性能优化实践指南

《深入解析JavaNIO在高并发场景下的性能优化实践指南》随着互联网业务不断演进,对高并发、低延时网络服务的需求日益增长,本文将深入解析JavaNIO在高并发场景下的性能优化方法,希望对大家有所帮助... 目录简介一、技术背景与应用场景二、核心原理深入分析2.1 Selector多路复用2.2 Buffer

Java Stream 的 Collectors.toMap高级应用与最佳实践

《JavaStream的Collectors.toMap高级应用与最佳实践》文章讲解JavaStreamAPI中Collectors.toMap的使用,涵盖基础语法、键冲突处理、自定义Map... 目录一、基础用法回顾二、处理键冲突三、自定义 Map 实现类型四、处理 null 值五、复杂值类型转换六、处理