当前位置: 首页 > news >正文

Typescript高级: 深入理解Partial类型和Required类型

Partial概述

  • 在 TypeScript 中,Partial 是一个非常实用的内置类型工具
  • 它允许我们轻松地将某个类型的所有属性变为可选的
  • 这对于处理可能缺少某些属性的对象时特别有用
  • 然而,Partial 背后的概念和它在实际开发中的应用场景远比表面看起来要丰富
  • Partial 是 TypeScript 中一个非常实用的类型工具,它允许我们轻松地将类型的所有属性变为可选的
  • 通过深入理解 Partial 的原理和使用场景,我们可以更有效地利用它来提高代码的可读性和可维护性
  • 同时,我们也需要注意它的潜在缺点和限制,以确保在项目中合理使用它

Partial 应用示例


1 ) Partial 的基本定义和使用

type Partial<T> = {[P in keyof T]?: T[P];
};
  • 这个定义使用了映射类型(Mapped Types)和条件类型(Conditional Types)的语法
  • keyof T 获取了类型 T 的所有属性键,然后 [P in keyof T]?: T[P] 为这些属性键中的每一个创建了一个新的可选属性

2 )使用 Partial 可以很容易地将一个类型的所有属性变为可选的

interface Person {  name: string;  age: number;  address: string;  
}  type PartialPerson = Partial<Person>;  const partialPerson: PartialPerson = {  name: "Alice",  // age 和 address 是可选的,所以可以省略,其实所有属性都是可选的 
};

3 )结合其他泛型

type OptionalRecord<K extends keyof any, T> = Partial<Record<K, T>>;  type OptionalStringRecord = OptionalRecord<'a' | 'b' | 'c', string>;  const obj: OptionalStringRecord = {  a: 'hello',  // b 和 c 是可选的  
};
  • Partial 可以与 TypeScript 中的其他泛型结合使用,以创建更复杂的类型
  • 例如,我们可以使用 Record 类型来创建一个具有特定属性集的对象
  • 并使用 Partial 来使这些属性变为可选的

4 ) 用于函数参数

  • 在编写函数时,我们可能希望某些参数是可选的

  • 使用 Partial 可以使这变得简单

    function updatePerson(person: Person, updates: Partial<Person>) {  return { ...person, ...updates };  
    }  const updatedPerson = updatePerson({ name: 'Bob', age: 30, address: '123 St.' }, { age: 31 });
    

5 ) 与其他类型工具结合

  • Partial 可以与其他类型工具如 Omit、Pick 等结合使用, 以创建更精细的类型控制

    type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;  type PersonDetails = Omit<Person, 'name'>;  
    type PartialPersonDetails = Partial<PersonDetails>;  const details: PartialPersonDetails = {  age: 25, // address 也是可选的  
    };
    

深入理解 Partial 的作用

  • Partial 的主要作用是提供了一种方便的方式来表示一个类型的“部分”或“子集”
  • 这在处理可能不完整或不确定的数据时非常有用,然而,它也有一些潜在的缺点和注意事项
  • 类型安全性
    • 虽然 Partial 使得属性变得可选,但它也降低了类型安全性
    • 在某些情况下,我们可能希望确保某些属性总是存在的
  • 可读性
    • 过度使用 Partial 可能会使代码的类型签名变得复杂和难以阅读
  • 性能
    • 虽然在现代 JavaScript 引擎中,类型检查的性能通常不是问题
    • 但在大型项目中,过多的类型操作可能会影响编译时间

Required 概述

  • Required 类型工具是一个很有用的内置特性,它允许我们创建一个新的类型,该类型中的所有属性都是必需的
  • 这在我们需要确保某个对象具有特定类型的所有属性时特别有用

Required 的基本用法


1 ) 在 TypeScript 中,Required 类型是通过以下方式定义的

type Required<T> = {[P in keyof T]-?: T[P];  
};
  • 这里的 keyof T 用于获取类型 T 的所有属性键
  • 而 -? 符号用于移除这些属性上的可选性(即,将它们从可选变为必需)

2 ) 使用 Required 可以很容易地将一个类型的所有属性变为必需的

interface PartialPerson {  name?: string;  age?: number;  address?: string;  
}  type RequiredPerson = Required<PartialPerson>;  const person: RequiredPerson = {  name: "Alice",  age: 30,  address: "123 St."  
};  // 如果没有提供所有属性,TypeScript 将会报错  
// const incompletePerson: RequiredPerson = { name: "Bob" }; // Error: Property 'age' is missing in type '{ name: string; }' but required in type 'RequiredPerson'.

3 ) 结合其他泛型

type PersonSubset = Pick<RequiredPerson, 'name' | 'age'>;  const subsetPerson: PersonSubset = {  name: "Charlie",  age: 25  
};  // 如果缺少 'name' 或 'age' 中的任何一个,TypeScript 将会报错  
// const missingAge: PersonSubset = { name: "David" }; // Error: Property 'age' is missing in type '{ name: string; }' but required in type 'PersonSubset'.
  • Required 可以与 TypeScript 中的其他泛型结合使用,以创建更复杂的类型
  • 例如,我们可以结合 Pick 类型来创建一个具有特定属性集的对象,并确保这些属性是必需的

4 ) 用于函数参数

function printPersonDetails(person: PartialPerson) {  const requiredPerson: RequiredPerson = person as RequiredPerson; // 注意:这里使用了类型断言,可能会导致运行时错误  // 现在我们可以安全地访问所有属性,因为它们都是必需的  console.log(`Name: ${requiredPerson.name}, Age: ${requiredPerson.age}, Address: ${requiredPerson.address}`);  
}  // 但是,更好的做法是在函数内部进行必要的检查  
function printPersonDetailsSafe(person: PartialPerson) {  if (person.name && person.age && person.address) {  console.log(`Name: ${person.name}, Age: ${person.age}, Address: ${person.address}`);  } else {  console.log('Incomplete person data');  }  
}
  • 当我们定义一个函数时,可能会接受一个具有可选属性的对象作为参数
  • 然而,在函数内部处理该对象时,我们可能希望确保该对象具有所有必需的属性
  • 这时,可以使用 Required 来转换参数类型

与其他类型工具结合

  • Required 还可以与其他类型工具如 Omit、Exclude 等结合使用,以创建更精细的类型控制
  • 例如,我们可以使用 Omit 去除某个类型的某些属性,然后使用 Required 确保剩余的属性都是必需的

相关文章:

  • linux tomcat版本漏洞升级
  • B站如何屏蔽短视频:成都鼎茂宏升文化传媒公司
  • 从零开始实现自己的串口调试助手(3) - 显示底部收发,优化串口打开/关闭
  • Gitlub如何删除分支(删除远程分支+本地分支)
  • 彻底卸载Windows Defender
  • 童心与美食的邂逅,蒙自源邀你共绘梦想画卷
  • 使用YOLOv10训练自己的数据集
  • Java基础八股
  • opencv笔记(13)—— 停车场车位识别
  • 银行批量支付业务功能测试点详细介绍
  • C语言编程零基础:开启编程之旅的起点
  • 乡镇围栏数据存储ES实践
  • MySQL之创建高性能的索引(十二)
  • 设计模式(五)结构型模式---适配器模式
  • Unity DOTS技术(三)JobSystem+Burst+批处理
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • AngularJS指令开发(1)——参数详解
  • bearychat的java client
  • CSS相对定位
  • eclipse的离线汉化
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • javascript面向对象之创建对象
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • vue.js框架原理浅析
  • Yii源码解读-服务定位器(Service Locator)
  • 给Prometheus造假数据的方法
  • 漂亮刷新控件-iOS
  • 深入浏览器事件循环的本质
  • 使用agvtool更改app version/build
  • 正则与JS中的正则
  • 你对linux中grep命令知道多少?
  • ‌移动管家手机智能控制汽车系统
  • (2)(2.10) LTM telemetry
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (八十八)VFL语言初步 - 实现布局
  • (补)B+树一些思想
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (三十)Flask之wtforms库【剖析源码上篇】
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十三)Flink SQL
  • (四)Controller接口控制器详解(三)
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)Sql Server 保留几位小数的两种做法
  • .net core 外观者设计模式 实现,多种支付选择
  • .NET MVC之AOP
  • .NET 的程序集加载上下文
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • .skip() 和 .only() 的使用
  • @Transient注解