實現 TypeScript 內置工具類型(源碼解析與實現)

目標讀者:已經熟悉 TypeScript 基礎語法、泛型、條件類型的同學。本文按常見工具類型的分類與順序實現并解釋 PartialRequiredReadonlyPickOmitRecordExcludeExtractNonNullableReturnTypeParametersConstructorParametersInstanceTypeThisParameterTypeOmitThisParameter


目錄(與上一篇順序一致)

  1. Partial
  2. Required
  3. Readonly
  4. Pick
  5. Omit
  6. Record
  7. Exclude
  8. Extract
  9. NonNullable
  10. ReturnType
  11. Parameters
  12. ConstructorParameters
  13. InstanceType
  14. ThisParameterType
  15. OmitThisParameter

注意:以下實現主要用于教學與閱讀(與 TypeScript 官方實現甚為相近),可幫助理解背后的類型技巧(映射類型、條件類型、inferkeyof 等)。


1. Partial<T>

用途回顧:把類型 T 的所有屬性變為可選。

實現思路:使用映射類型把每個屬性的修飾符 ? 添加上。

type MyPartial<T> = {[K in keyof T]?: T[K];
};// 示例
interface User { id: number; name: string }
type PUser = MyPartial<User>; // { id?: number; name?: string }

要點[K in keyof T] 遍歷 T 的所有鍵,?: 表示可選屬性。


2. Required<T>

用途回顧:把 T 的所有屬性變為必選。

實現思路:與 Partial 相反,移除可選修飾符 ?

type MyRequired<T> = {[K in keyof T]-?: T[K];
};// 示例
interface Opt { a?: number }
type R = MyRequired<Opt>; // { a: number }

要點-? 是映射類型的語法,用來移除可選標記。


3. Readonly<T>

用途回顧:把 T 的所有屬性變為只讀。

實現思路:使用映射類型并加上 readonly 修飾符。

type MyReadonly<T> = {readonly [K in keyof T]: T[K];
};// 示例
type R = MyReadonly<{ x: number }>; // { readonly x: number }

要點readonly?-? 一樣都是映射類型可用的修飾符。


4. Pick<T, K>

用途回顧:從 T 中挑選一部分屬性 KK 是鍵的聯合類型)。

實現思路:遍歷 Kextends keyof T),并把對應屬性取出。

type MyPick<T, K extends keyof T> = {[P in K]: T[P];
};// 示例
interface User { id: number; name: string; age: number }
type Preview = MyPick<User, 'id' | 'name'>; // { id: number; name: string }

要點K extends keyof T 約束保證 K 只包含 T 的鍵。


5. Omit<T, K>

用途回顧:從 T 中排除某些屬性 K

實現思路Omit<T, K> 通常等價于從 T 的鍵中 Exclude<keyof T, K>,然后 Pick 出剩余的鍵。

type MyOmit<T, K extends keyof any> = MyPick<T, Exclude<keyof T, K>>;// 或者直接寫成:
// type MyOmit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P] }// 示例
interface User { id: number; name: string; password: string }
type Safe = MyOmit<User, 'password'>; // { id: number; name: string }

要點K extends keyof any(或 keyof T)使得 K 可以是字符串字面量等;Exclude 在后面會解釋。


6. Record<K, T>

用途回顧:構造一個以聯合類型 K 為鍵,值為 T 的對象類型。

實現思路:映射類型直接遍歷 K

type MyRecord<K extends keyof any, T> = {[P in K]: T;
};// 示例
type Roles = 'admin' | 'user';
type RoleCount = MyRecord<Roles, number>; // { admin: number; user: number }

要點keyof any 表示允許任意作為 object key 的類型(string | number | symbol)。


7. Exclude<T, U>

用途回顧:從聯合類型 T 中排除能賦值給 U 的成員。

實現思路Exclude 是分布式條件類型(conditional type)的一種應用:

// 分布式條件類型:當 T 是聯合類型時,條件類型會對聯合的每個成員逐一計算
// 例如: T = A | B, 則 T extends U ? X : Y 會計算 A extends U ? X : Y 以及 B extends U ? X : Y,然后把結果聯合在一起type MyExclude<T, U> = T extends U ? never : T;// 示例
type E = MyExclude<'a' | 'b' | 'c', 'a' | 'f'>; // 'b' | 'c'

要點Exclude 利用了條件類型的“分布式”特性:當 T 是聯合類型時,T extends ... 會分解處理每個成員。


8. Extract<T, U>

用途回顧:從 T 中提取可賦值給 U 的成員(交集)。

實現思路:和 Exclude 相反:

type MyExtract<T, U> = T extends U ? T : never;// 示例
type X = MyExtract<'a' | 'b' | 'c', 'a' | 'f'>; // 'a'

要點:同樣利用了條件類型的分布式行為。


9. NonNullable<T>

用途回顧:移除 nullundefined

實現思路:等價于 Exclude<T, null | undefined>

type MyNonNullable<T> = MyExclude<T, null | undefined>;// 示例
type N = MyNonNullable<string | null | undefined>; // string

要點:這是組合前面工具類型的好例子。


10. ReturnType<T>

用途回顧:獲取函數類型 T 的返回值類型。

實現思路:使用 infer 在條件類型中提取返回類型。

type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;// 示例
function foo() { return { x: 1 } }
type FooRet = MyReturnType<typeof foo>; // { x: number }

要點infer R 用來聲明并捕獲返回類型。


11. Parameters<T>

用途回顧:獲取函數類型 T 的參數類型元組。

實現思路:同樣用 infer 提取參數元組 P

type MyParameters<T> = T extends (...args: infer P) => any ? P : never;function greet(a: string, b: number) {}
type G = MyParameters<typeof greet>; // [string, number]

要點:通過 infer P 捕獲參數列表的類型元組。


12. ConstructorParameters<T>

用途回顧:獲取構造函數類型(類/構造簽名)的參數元組。

實現思路:這里 Tnew (...args: any) => any 的構造簽名;用 infer 提取構造參數。

type MyConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;class Person { constructor(name: string, age: number) {} }
type C = MyConstructorParameters<typeof Person>; // [string, number]

要點abstract new 是為了兼容普通類與抽象構造簽名。


13. InstanceType<T>

用途回顧:給定一個構造函數類型 T,返回其實例類型。

實現思路:使用條件類型匹配 new (...args: any) => infer R,返回實例 R

type MyInstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;class Person { name = 'tom' }
type P = MyInstanceType<typeof Person>; // Person

要點InstanceType 常用于庫設計或工廠模式中從類類型推導實例類型。


14. ThisParameterType<T>

用途回顧:提取函數類型中的 this 參數類型(如果有)。

實現思路:匹配 this: X 形式的函數簽名并用 infer 捕獲 X

type MyThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;function fn(this: { name: string }, a: number) { return this.name }
type ThisT = MyThisParameterType<typeof fn>; // { name: string }

要點:如果函數沒有 this 參數,官方實現會返回 unknown


15. OmitThisParameter<T>

用途回顧:移除函數類型中的 this 參數,得到普通函數類型(用于 bind/call 時的 type convenience)。

實現思路:如果函數包含 this 參數,將其轉換為不含 this 的函數類型。

type MyOmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (this: any, ...args: infer P) => infer R ? (...args: P) => R : T;// 示例
function say(this: { name: string }, n: number) { return this.name + n }
type FnNoThis = MyOmitThisParameter<typeof say>; // (n: number) => string

要點:實現里先檢查 ThisParameterType<T> 是否為 unknown(即函數沒有顯式 this),如果是就直接返回原類型 T;否則提取參數與返回值并重構為不帶 this 的函數類型。


額外:官方實現 vs 教學實現差異

  • 官方的實現會有更多兼容性考量、any/unknown 微妙行為處理,以及對 TS 版本特性的更細致支持(例如 abstract newthis 分支的邊界情況)。
  • 教學實現避免極端兼容性,為了可讀性而做了簡化,但核心思想一致。

小結

通過實現這些常用工具類型,你可以更清楚地理解:

  • 映射類型[K in keyof T])是如何構造新類型的;
  • 條件類型與其分布式特性如何在聯合類型上逐項計算;
  • infer 如何在條件類型中提取內部類型(函數參數、返回值、Promise 的包裹類型等)。

掌握這些技巧之后,你可以讀懂并自己實現更復雜的工具類型,寫出更類型安全、可復用的代碼庫。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/97837.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/97837.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/97837.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Boot + Nacos 配置中心示例工程

1?? 工程結構 nacos-demo├── pom.xml└── src├── main│ ├── java│ │ └── com.example.nacosdemo│ │ ├── NacosDemoApplication.java│ │ ├── config│ │ │ └── AppProperties.java│ │ └── cont…

(二)文件管理-基礎命令-pwd命令的使用

文章目錄1. 命令格式2. 基本用法3. 高級用法4. 注意事項1. 命令格式 pwd [OPTION]...[OPTION]: 可選選項&#xff0c;用于改變命令的默認行為。最主要的兩個選項是 -L 和 -P。它不需要任何參數&#xff08;如文件名或目錄名&#xff09; 2. 基本用法 用法&#xff1a;pwd 是…

Leetcode_202.快樂數_三種方法解決(普通方法解決,哈希表解決,循環鏈表的性質解決_快慢指針)

目錄第一種方法&#xff1a;暴力解法暴力ac代碼&#xff1a;第二種方法&#xff1a;哈希表哈希表ac代碼:第三種方法&#xff1a;根據循環鏈表的性質(快慢指針)第一種方法&#xff1a;暴力解法 最暴力的思路就是直接使用循環往下一直計算&#xff0c;這樣特別浪費時間&#xff…

代碼隨想錄刷題Day48

這次博客主要是對做過的關于二叉樹系列的題目進行整理和分類。二叉樹&#xff0c;要處理整個樹&#xff0c;一般少不了遍歷。遍歷主要可以分為&#xff1a;遞歸系列、層序遍歷。如果不遍歷的話&#xff0c;那就是處理特殊的樹了&#xff0c;比如完全二叉樹。遞歸系列基本的遞歸…

汽車工裝結構件3D掃描尺寸測量公差比對-中科米堆CASAIM

汽車制造過程中&#xff0c;工裝結構件的尺寸精度對整車裝配質量和生產進度有重要影響。傳統測量工具如卡尺和三坐標測量機采用接觸式工作方式&#xff0c;檢測過程耗時較長&#xff0c;對于具有復雜曲面特征的工件&#xff0c;難以全面獲取尺寸數據。激光三維掃描技術改變了傳…

Docker Pull 代理配置方法

本文介紹通過網絡代理加速Docker鏡像拉取的方法。 配置方法 當執行docker pull從Docker Hub 拉取鏡像時&#xff0c;其網絡連接由守護進程docker daemon進行維護。 要修改其代理設置&#xff0c;可配置其systemd服務&#xff0c;步驟如下&#xff1a; &#xff08;1&#xf…

機電裝置:從基礎原理到前沿應用的全方位解析

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 1 機電裝置的基本概念與發展歷程 機電裝置&#xff08;Mechatronic D…

《SVA斷言系統學習之路》【03】關于布爾表達式

序列中使用的表達式基于其所含變量的采樣值進行評估。表達式評估的結果為布爾值&#xff0c;其解釋方式與過程性if語句條件中的表達式完全相同&#xff1a;若表達式計算結果為X、Z 或 0&#xff0c;則被解釋為假&#xff1b;否則即為真。但是&#xff0c;對可出現在并發斷言中的…

指針高級(2)

6.數組指針#include <stdio.h> int main() {/*練習&#xff1a;利用指針遍歷數組*///1.定義數組int arr[] { 10,20,30,40,50 };int len sizeof(arr) / sizeof(int);//2.獲取數組的指針//實際上獲取的&#xff1a;數組的首地址int* p1 arr;int* p2 &arr[0];printf…

如何高效記單詞之:抓住首字母——以find、fund、fond、font為例

find、fund、fond、font這幾個單詞&#xff0c;你都認識嗎&#xff1f;這幾個單詞&#xff0c;意思大體如下&#xff1a; find v.找到&#xff1b;發現fund n.基金fond a.喜歡的&#xff1b;喜愛的&#xff1b;深情的font n.字體&#xff0c;字型&#xff0c;字形 這幾個單詞在…

Ubuntu下把 SD 卡格式化為 FAT32

在 Ubuntu 下把 SD 卡格式化為 FAT32&#xff0c;按下面做&#xff08;會抹掉整卡數據??&#xff09;&#xff1a; 1) 找到你的 SD 卡設備名 lsblk -p記下整盤設備&#xff0c;比如 /dev/sdb&#xff08;USB 讀卡器常見&#xff09;或 /dev/mmcblk0&#xff08;內置讀卡器&am…

涉私數據安全與可控匿名化利用機制研究(上)

文章目錄前言一、涉私數據的概述及分類&#xff08;一&#xff09;涉私數據的“知情同意原則”&#xff08;二&#xff09;涉私數據的分類二、涉私數據可控匿名化利用機制&#xff08;一&#xff09;數據產品與涉私數據的利用形式&#xff08;二&#xff09;通過可信數據空間受…

Redis 的跳躍表:像商場多層導航系統一樣的有序結構

目錄 一 、從 "超市貨架" 的痛點看跳躍表的價值 1.1、跳躍表與商場導航系統的結構對應 1. 1.1、zskiplistNode&#xff1a;帶導航標記的 "商品"&#xff08;跳躍表節點&#xff09; 1.1.1.1、level []&#xff1a;商品上的多層導航標記 1.1.1.2、back…

小程序點擊之數據綁定

<return /><view class"all-wrap" style"padding-top:{{topHeight}}px;"><view class"my-title">我的收藏</view><scroll-viewclass"collect-list-container"scroll-yscroll-top"{{scrollTop}}"…

數據結構——順序表和單向鏈表(2)

目錄 前言 一、單向鏈表 1、基本概念 2、單向鏈表的設計 &#xff08;1&#xff09;節點設計 &#xff08;2&#xff09;初始化空單向鏈表 &#xff08;3&#xff09;、初始化數據節點 &#xff08;4&#xff09;數據節點 &#xff08;5&#xff09;判斷鏈表是否為空 …

More Effective C++ 條款26:限制某個類所能產生的對象數量

More Effective C 條款26&#xff1a;限制某個類所能產生的對象數量核心思想&#xff1a;通過控制類的實例化過程&#xff0c;限制程序中該類的對象數量&#xff0c;可以防止資源過度使用&#xff0c;確保系統資源合理分配&#xff0c;并實現單例或有限實例模式。 &#x1f680…

CMS系統維護中常見的安全威脅及防護指南!

內容管理系統&#xff08;CMS&#xff09;已成為網站建設的核心工具&#xff0c;但隨之而來的安全風險卻常被低估。超過70%的網站使用CMS構建&#xff0c;而其中近半數曾遭遇安全漏洞威脅。作為運維人員和開發者&#xff0c;了解這些安全威脅并采取相應防護措施至關重要。 一、…

springboot knife4j 接口文檔入門與實戰

Spring Boot3 Knife4j 項目地址https://gitee.com/supervol/loong-springboot-study&#xff08;記得給個start&#xff0c;感謝&#xff09;Knife4j 介紹在國內 Java 開發領域&#xff0c;Knife4j 是一款廣受歡迎的 API 文檔工具&#xff0c;它基于 OpenAPI 規范&#xff0c;在…

Spring Boot 事務失效的八大原因及解決方案詳解

在 Spring Boot 項目開發中&#xff0c;聲明式事務管理通過 Transactional 注解提供了極大的便利。但許多開發者都曾遇到過事務不生效的困擾。本文將詳細分析導致 Spring Boot 事務失效的八大常見情況&#xff0c;并提供相應的解決方案。1. 數據庫引擎不支持事務問題分析&#…

數據結構:順序棧與鏈棧的原理、實現及應用

數據結構詳解&#xff1a;順序棧與鏈棧的原理、實現及應用 1. 引言&#xff1a;棧的核心概念 棧&#xff08;Stack&#xff09;是一種重要的線性數據結構&#xff0c;它遵循后進先出&#xff08;Last In First Out, LIFO&#xff09;的原則。這意味著最后一個被添加到棧中的元素…