Skip to content

Commit

Permalink
2023/8/8
Browse files Browse the repository at this point in the history
  • Loading branch information
a1392558812 committed Aug 8, 2023
1 parent 3aa2a59 commit ee92bd3
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 103 deletions.
38 changes: 21 additions & 17 deletions 03.杂项整理/08.any、unknown、never、void区别.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
## any、unknown、never、void 区别

- any: 用于描述任意类型的变量,不作任何约束,编译时会跳过对其的类型检查

- unknow: 表示未知类型,即写代码的时候还不知道具体会是怎样的数据类型
- never: 用不存在的值的类型,常用于表示永不能执行到终点的函数返回值,例如抛出异常或函数中执行无限循环的代码(死循环)的函数返回值类型(这个类型表示的是哪些永不存在的值的类型, 应用的地方相对比较少,一般如果用 Never 去描述一个函数的返回值, 那么这个函数就不能有任何返回出现, 也就是说这个函数必须不能正常结束, 所以我们就需要在该函数内让函数代码报错, 就是手动抛出异常, 或者让函数永不结束。)

- never: 用不存在的值的类型,常用于表示永不能执行到终点的函数返回值,例如抛出异常或函数中执行无限循环的代码(死循环)的函数返回值类型(这个类型表示的是哪些永不存在的值的类型, 应用的地方相对比较少,一般如果用 Never 去描述一个函数的返回值, 那么这个函数就不能有任何返回出现, 也就是说这个函数必须不能正常结束, 所以我们就需要在该函数内让函数代码报错, 就是手动抛出异常, 或者让函数永不结束。)

- ```typescript
// 不相交类型的inteserction结果为never:
type result = 1 & 2; // 结果为never
```

尤大佬在知乎的一段回答,很有真知灼见

当你有一个 union type:

```typescript
interface Foo {
type: 'foo';
}
interface Bar {
type: 'bar';
}
type All = Foo | Bar;
```

switch 当中判断 typeTS 是可以收窄类型的 (discriminated union):

```typescript
function handleValue(val: All) {
switch (val.type) {
Expand All @@ -43,17 +45,17 @@
}
}
```

注意在 default 里面我们把被收窄为 neverval 赋值给一个显式声明为 never 的变量。如果一切逻辑正确,那么这里应该能够编译通过。但是假如后来有一天你的同事改了 All 的类型:

```typescript
type All = Foo | Bar | Baz;
```

然而他忘记了在 handleValue 里面加上针对 Baz 的处理逻辑,这个时候在 default branch 里面 val 会被收窄为 Baz,导致无法赋值给 never,产生一个编译错误。所以通过这个办法,你可以确保 handleValue 总是穷尽 (exhaust) 了所有 All 的可能类型。

- void: 表示无任何类型,没有类型,例如没有返回值的函数的返回值类型

- ```typescript
// JavaScript 没有空值(void)的概念,在 TypeScript 中,表示没有任何返回值的函数
// 没有返回值的函数,其返回值类型为 void
Expand All @@ -62,17 +64,17 @@
}
// 当然你也可以声明一个变量为void ,但你只能将它赋值为 undefined 和 null:
let unusable: void = undefined;
// 如果什么都不写,此时,add 函数的返回值类型为: void
const add = () => {
//
};
// 如果return之后什么都不写,此时,add 函数的返回值类型为: void
const add = () => {
return;
};
const add = (): void => {
// 此处,返回的 undefined 是 JS 中的一个值return undefined
};
Expand All @@ -83,14 +85,16 @@
```

- anyunknown 的区别:

- unknowany 类似,但使用前必须进行断言或守卫

- nevervoid 的区别:

- 用于函数时,never 表示函数用于执行不到返回值那一步(抛出异常或死循环)的返回值类型,即永不存在的值的类型,而 void 则表示没有返回值,不返回或返回 undefined

> - 能不用 any 就不用 any
>
> * 声明时如果不确定具体的类型,则可以使用 unknow 代替,在使用时用类型断言或类型守卫进行类型收缩
>
> * never 常用于构造条件类型来组合出更灵活的类型定义
>
> * void 常用于表示函数没有返回值
18 changes: 11 additions & 7 deletions 03.杂项整理/09.type和interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
- interface 接口是命名数据结构(例如对象)的另一种方式;与 type 不同,interface 仅限于描述对象类型。接口的声明语法也不同于类型别名的声明语法。

- 相同之处

- 都可以用来定义对象类型的属性和方法

- type 和 interface 并不互斥。type 可以继承 interface,反之亦然。只是在实现形式上,稍微有些区别,对于 interface 来说,继承是通过 extends 实现的;而 type 是通过 & 来实现的,也可以叫做交叉类型。

- ```typescript
// type 可以使用联合类型(|)和交叉类型(&)进行类型组合。
// interface 不支持联合类型和交叉类型。
type Status = 'success' | 'error';
type Point = { x: number } & { y: number };
```

- ```typescript
// interface 继承 interface
interface Person {
Expand All @@ -29,19 +31,21 @@
```

- 不同之处

- type 可以做到而 interface 不能做到

- type 不能直接用于类的实现。
- type 可以声明基本类型。 `type userName = string;`
- type 可以声明联合类型。 `type userMsg = string | number; `
- type 可以声明元组类型。 `type Data = [number, string];`
- type 可以通过 typeof 操作符来声明类型 `type myType = typeof someObj;`

- interface 可以做到而 type 不能做到

- interface 可以用于定义类的实现,包括属性和方法。

- interface 可以声明合并。

- ```typescript
// 如果是 type 的话,就会报重复定义的警告,因此是无法实现声明合并的。
interface test {
Expand All @@ -50,7 +54,7 @@
interface test {
age: number;
}

/*
test实际为 {
name: string
Expand Down
44 changes: 39 additions & 5 deletions 03.杂项整理/10.never类型的常用场景.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,62 @@
> 一个总是会抛出错误的函数(如:function foo() { throw new Error('Not Implemented') },foo 的返回类型是 never);
- 属性互斥,当需要二选一时,可以通过联合类型,将互斥的两个属性分别标注为可选的 never 类型

- ```typescript
interface A {
name: string;
id?: never;
}

interface B {
id: string;
name?: never;
}

const useT1: A | B = {
name: 'spp',
id: '',
}; //error

const useT2: A | B = {
name: 'spp',
};

const useT3: A | B = {
id: '28',
};
```
* 完整性检查

```typescript
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
ra//dius: number;
}
type Shape = Square | Rectangle | Circle;
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
default: return assertNever(s); // error here if there are missing cases
}
}
// 当没有涵盖所有可辨识联合的变化时,我们想让编译器可以通知我们。 比如,如果我们添加了 Triangle到 Shape,我们同时还需要更新 area:
// type Shape = Square | Rectangle | Circle | Triangle;
```
42 changes: 42 additions & 0 deletions 03.杂项整理/13.泛型约束.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
## 泛型约束

```typescript
interface Person {
name: string;
}
// extends:扩展;延长;扩大;使延期;使伸长;扩大…的范围(或影响),extend的第三人称单数
// 即通过extends关键字可以对类型进行进一步拓展约束范围
// 简单的理解,就是“使...必须包含....”, 则接收的类型参数T,必须包含类型Person所要求的
const test = <T extends Person>(str: T) => str;
test({ name: 'Awen' }); // Ok name是必传的
test({ name: 'Awen', age: 666 }); // Ok,age是额外传递的,即使类型Person不要求age参数
```

```typescript
type Msg<T> = T extends { msg: unknown } ? T['msg'] : never;
const msg1: Msg<{ msg: string }> = '666';
const msg2: Msg<{ msg: number }> = 666;
const msg: Msg<{ msg: '你好啊' }> = '你好啊';
```

```typescript
type Square = {
kind: 'square';
x: number;
y: number;
};
type Circle = {
kind: 'circle';
radius: number;
};

type GeneratorKindFunction<T extends { kind: string }> = {
[K in T['kind']]: (obj: T extends { kind: K } ? T : never) => number;
};
type newType = GeneratorKindFunction<Square | Circle>;

/**
* type newType = { square: (str: Square) => number; circle: (obj: Circle) => number; }
*
*/
```
62 changes: 62 additions & 0 deletions 03.杂项整理/14.类型保护工具函数.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## 类型保护工具函数

```typescript
export function isString(value: unknown): value is string {
return typeof value === 'string';
}

export function isNumber(value: unknown): value is number {
return typeof value === 'number';
}

export function isBoolean(value: unknown): value is boolean {
return typeof value === 'boolean';
}

export function isFunction(value: unknown): value is Function {
return typeof value === 'function';
}

export function isObject(value: unknown): value is object {
return value !== null && typeof value === 'object';
}

export function isArray(value: unknown): value is unknown[] {
return Array.isArray(value);
}

export function isDate(value: unknown): value is Date {
return value instanceof Date;
}

export function isPromise(value: unknown): value is Promise<any> {
return (
!!value &&
typeof value === 'object' &&
typeof (value as Promise<any>).then === 'function'
);
}

export function isSymbol(value: unknown): value is symbol {
return typeof value === 'symbol';
}

export function isUndefined(value: unknown): value is undefined {
return value === undefined;
}

export function isNull(value: unknown): value is null {
return value === null;
}

export function isBigInt(value: unknown): value is BigInt {
return typeof value === 'bigint';
}

export function isInstanceOf<T>(
value: unknown,
constructor: new (...args: any[]) => T
): value is T {
return value instanceof constructor;
}
```
41 changes: 41 additions & 0 deletions 03.杂项整理/15.联合类型和交叉类型理解.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## 联合类型和交叉类型理解

> 这里,我个人的一个理解,
>
> 联合类型:`type C = A | B | .....`。那么类型C需要满足的条件是符合类型(**A 、 B 、 .....**)中的一项约束即可,也可满足n项约束。可以理解为 T extends A|B|C|D.....Z
>
> 交叉类型:`type D = A & B & .....`。那么类型D需要满足的条件是符合类型(**自类型A开始到最后一项**)中的每一项类型的约束,可以理解为(((((T extends A) extends B) extends C) extends D).....) extends Z, 即`<T extends A & B & C & .... & Z>`
### 联合类型 (Union Types):

* 联合类型表示一个变量可以具有多个类型中的一个。使用 | 符号将各个类型分隔开,例如 type1 | type2 | type3。

* 联合类型适用于在特定场景中可能是多种类型的变量或参数。

* 使用联合类型时,您需要注意以下几点:

* 联合类型只能访问共同的属性和方法。即只能操作每个类型可能具有的公共成员。
* 当对一个联合类型的变量进行操作时,只能使用所有可能类型的共有成员。这意味着可能需要使用类型断言(Type Assertion)来告诉编译器您正在操作的是某个具体的类型。或者使用类型保护机制,如类型谓词、typeof 检查、instanceof 检查等。
* 联合类型的值在使用时可能存在不确定性,因此需要再次检查和处理不同类型的值。

### 交叉类型 (Intersection Types):

* 交叉类型表示一个值具有多个类型的属性和方法。使用 & 符号将多个类型合并在一起,例如 type1 & type2 & type3。

* 交叉类型适用于需要同时满足多个类型特征的情况。

* 使用交叉类型时,您需要注意以下几点:

* 交叉类型允许对象同时具备多个类型所定义的属性和方法。
* 当您使用交叉类型时,您可以直接访问所有类型的成员,而无需进行类型断言或类型保护。
* 如果交叉类型的多个类型具有相同的属性,属性的类型将是联合类型。例如,如果一个类型具有属性 a 的类型为 number,而另一个类型具有属性 a 的类型为 string,则交叉类型中的属性 a 的类型将为 number | string。

* 下面是一些使用联合类型和交叉类型时的注意事项:

* 联合类型和交叉类型可以用于类型声明和类型别名。

* 在实际使用中,请确保合理并准确地定义联合类型和交叉类型,以避免类型错误。

* 对于联合类型,请确定您已经正确处理了所有可能的类型取值,并在必要时进行类型检查和处理。

* 对于交叉类型,请确保所组合的类型在逻辑上兼容,并且您需要的属性和方法在每个类型中都存在。
Loading

0 comments on commit ee92bd3

Please sign in to comment.