Skip to content

Commit

Permalink
doc: update
Browse files Browse the repository at this point in the history
  • Loading branch information
jiaziling committed Oct 18, 2024
1 parent 9a304f9 commit cd45104
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: ts类型体操之工具函数Equal的实现原理
description: ts类型体操之工具函数Equal的实现原理
date: 2024-10-18
---

**实现源码**
```typescript
export type Equal<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => 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<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => 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中增加等于运算符提案讨论》
58 changes: 57 additions & 1 deletion src/content/blog/fe/ts类型体操笔记(简单,中等).md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type ObjectToUnion<T> = T[keyof T]
```
---
- 斐波那契类型解题思路:
- 4192 题 Fibonacci 斐波那契类型解题思路:
- 使用 N1 N2 两个数组的**长度**代表当前参与计算的两个数
- 使用 No 数组来标记当前的计算位置,No 数组长度的增加意味着计算向右移动接近目标值
- 计算的第一位和第二位需要特殊处理,两者是基础值都为1
Expand All @@ -97,3 +97,59 @@ type Fibonacci<
```
---
---
- 4471 题 Zip
- 第一种解法:
```typescript
type Zip<A extends any[], B extends any[], L extends any[] = []> = L['length'] extends A['length'] | B['length']
? L
: Zip<A, B, [...L, [A[L['length']], B[L['length']]]]>
```
- 第二种解法:
```typescript
type Zip<T, U, N extends any[] = []> = T extends [infer TF, ...infer TR] ? U extends [infer UF, ...infer UR] ? [[TF, UF], ...Zip<TR, UR>] : 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 string> = S extends `${infer Left}${infer Right}` ? Right extends ' ' | '\n' | '\t' ? TrimRight<Left> : S : S;
```
上面的答案是错误的,应该是下面这样的:

```typescript
type TrimRight<S extends string> = S extends `${infer Left}${' ' | '\n' | '\t'}` ? TrimRight<Left> : S;
```
目前没能理解为什么会这样

----

- 判断两个类型是否相等的一些情况:
- 对于文字类型 A B,当且仅当 A extends B 时,A 才等于 B
- 两个无参数函数类型相等当且仅当它们的返回值类型相等
- 对于分别返回字面量类型 L1 和 L2 的无参数函数 F1 F2,当且仅当 L1 extends L2 时,F1 才等于 F2
- 当所有可以赋值给 X1 的类型 E1 的集合与所有可以赋值给 X2 的类型 E2 的集合相同时,两个类型 X1 和 X2 相等

0 comments on commit cd45104

Please sign in to comment.