From cd45104ad22491f403addd4a5c8cfa62bf1e5fad Mon Sep 17 00:00:00 2001 From: jiaziling Date: Fri, 18 Oct 2024 17:57:45 +0800 Subject: [PATCH] doc: update --- ...36\347\216\260\345\216\237\347\220\206.md" | 66 +++++++++++++++++++ ...5\357\274\214\344\270\255\347\255\211).md" | 58 +++++++++++++++- 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 "src/content/blog/fe/ts\347\261\273\345\236\213\344\275\223\346\223\215\344\271\213\345\267\245\345\205\267\345\207\275\346\225\260Equal\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" diff --git "a/src/content/blog/fe/ts\347\261\273\345\236\213\344\275\223\346\223\215\344\271\213\345\267\245\345\205\267\345\207\275\346\225\260Equal\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" "b/src/content/blog/fe/ts\347\261\273\345\236\213\344\275\223\346\223\215\344\271\213\345\267\245\345\205\267\345\207\275\346\225\260Equal\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" new file mode 100644 index 0000000..4754444 --- /dev/null +++ "b/src/content/blog/fe/ts\347\261\273\345\236\213\344\275\223\346\223\215\344\271\213\345\267\245\345\205\267\345\207\275\346\225\260Equal\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" @@ -0,0 +1,66 @@ +--- +title: ts类型体操之工具函数Equal的实现原理 +description: ts类型体操之工具函数Equal的实现原理 +date: 2024-10-18 +--- + +**实现源码** +```typescript +export type Equal = + (() => T extends X ? 1 : 2) extends + (() => T extends Y ? 1 : 2) ? true : false +``` + +**详解** +先来看可赋值性: +X extends Y ? true : false +如果返回为 true,说明 X 满足 Y 的结构,即 X 可被赋值给 Y。 + +那么,如果两个类型可以相互赋值,是否就说明二者相等? +```typescript +X extends Y ? Y extends X : true : false : false // 🤔 X和Y是相同类型? +``` + +来看个🌰: +```typescript +type A = { a:string, b?:boolean } +type B = { a:string, c?:number } + + +// 可赋值性 +type C = A extends B ? true : false // true +type D = B extends A ? true : false // true +``` + +由此可见,相互可赋值并不能判断两个类型相同,还需要其他的方式。 + +在 ts 的 checker.ts 源码中,有如下一段注释: + +:::note[源码注释] +// Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if +// one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2, +// and Y1 is related to Y2. +::: + +也就是说,如果想满足两个条件类型 T1 extends U1 ? X1 : Y1 和 T2 extends U2 ? X2 : Y2 具备可分配性,需要满足以下条件: + +- T1 和 T2 必须互相可分配。 +- U1 与 U2 必须完全相等。 +- X1 可分配给 X2 以及 Y1 可分配给 Y2 +即 `条件类型可分配性判断所需要满足的条件` + +回到文章开头的答案: +```typescript +export type Equal = + (() => T extends X ? 1 : 2) extends + (() => T extends Y ? 1 : 2) ? true : false +``` + +其中两个无参函数只是为了给条件类型语句提供一个泛型 T,而核心代码就是在判断 `T extends X ? 1: 2` 与 `T extends Y ? 1: 2` 的可分配性。 + +根据 `条件类型可分配性判断所需要满足的条件` 的描述, Equal 的实现方式要求 X 和 Y 必须全等否则就会返回 false,这种实现通过 hack 的方式触发 TS 检查器对语句进行判断从而保证 X 和 Y 的全等性。 + +以上。 + + +参考文案:https://github.com/microsoft/TypeScript/issues/27024《对于TS中增加等于运算符提案讨论》 \ No newline at end of file diff --git "a/src/content/blog/fe/ts\347\261\273\345\236\213\344\275\223\346\223\215\347\254\224\350\256\260(\347\256\200\345\215\225\357\274\214\344\270\255\347\255\211).md" "b/src/content/blog/fe/ts\347\261\273\345\236\213\344\275\223\346\223\215\347\254\224\350\256\260(\347\256\200\345\215\225\357\274\214\344\270\255\347\255\211).md" index de8cbc5..19fdc5f 100644 --- "a/src/content/blog/fe/ts\347\261\273\345\236\213\344\275\223\346\223\215\347\254\224\350\256\260(\347\256\200\345\215\225\357\274\214\344\270\255\347\255\211).md" +++ "b/src/content/blog/fe/ts\347\261\273\345\236\213\344\275\223\346\223\215\347\254\224\350\256\260(\347\256\200\345\215\225\357\274\214\344\270\255\347\255\211).md" @@ -71,7 +71,7 @@ type ObjectToUnion = T[keyof T] ``` --- -- 斐波那契类型解题思路: +- 4192 题 Fibonacci 斐波那契类型解题思路: - 使用 N1 N2 两个数组的**长度**代表当前参与计算的两个数 - 使用 No 数组来标记当前的计算位置,No 数组长度的增加意味着计算向右移动接近目标值 - 计算的第一位和第二位需要特殊处理,两者是基础值都为1 @@ -97,3 +97,59 @@ type Fibonacci< ``` --- +--- + +- 4471 题 Zip + +- 第一种解法: +```typescript +type Zip = L['length'] extends A['length'] | B['length'] + ? L + : Zip +``` + +- 第二种解法: +```typescript +type Zip = T extends [infer TF, ...infer TR] ? U extends [infer UF, ...infer UR] ? [[TF, UF], ...Zip] : N : N +``` + +第一种解法很巧妙,利用 L 数组的长度来标记当前对 A 和 B 的递归位置,当 L 长度等于 A 或 B 长度时,停止遍历,返回 L 数组。随着递归的进行,每次成功的递归都会使得 L 数组的长度增加 1,而这个 +1 后的 L['length'] 恰好可以用在下一次递归时作为索引来获取 A 和 B 的对应元素。 + +第二种解法是利用递归的思想,先判断 T 和 U 是否可以被拆分成第一个元素和其他剩余部分,如果 T 和 U 都可以,则将两个取出的第一个元素组成新的元组,并递归处理剩余部分 N;如果 T 或 U 不能被拆分,则直接返回上次递归传入的当前的 N 数组 + +--- +- 如何区分一个确切的 数字类型 还是 number 类型 + + - 确切的数字扩展自 number + - number 扩展自它自己 number + - number 不能扩展自确切的数字 + +Exact number extends number ? true : false // true +number extends number ? true : false // true +number extends Exact number ? true : false // false + +--- + +- 没能理解的 trimRight +- +:::tips[TODO] +why? +::: + +```typescript +type TrimRight = S extends `${infer Left}${infer Right}` ? Right extends ' ' | '\n' | '\t' ? TrimRight : S : S; +``` +上面的答案是错误的,应该是下面这样的: + +```typescript +type TrimRight = S extends `${infer Left}${' ' | '\n' | '\t'}` ? TrimRight : S; +``` +目前没能理解为什么会这样 + +---- + +- 判断两个类型是否相等的一些情况: + - 对于文字类型 A B,当且仅当 A extends B 时,A 才等于 B + - 两个无参数函数类型相等当且仅当它们的返回值类型相等 + - 对于分别返回字面量类型 L1 和 L2 的无参数函数 F1 F2,当且仅当 L1 extends L2 时,F1 才等于 F2 + - 当所有可以赋值给 X1 的类型 E1 的集合与所有可以赋值给 X2 的类型 E2 的集合相同时,两个类型 X1 和 X2 相等 \ No newline at end of file