Skip to main content

TypeScript 基础

1、 使用as const的作用

对象设置为 const 之后,不能修改属性值


const obj = {
a: 1
} as const;

// error
// Property 'b' does not exist on type '{ readonly a: 1; }'
obj.b = 1;

数组设置为 const 之后,不能修改长度

const arr = [1,2] as const;
// error
// Property 'push' does not exist on type 'readonly [1, 2]'
arr.push(3);


2、 Extract、Exclude、Pick、Omit 实现原理

2.1、 Extract

语法规则:

提取Type中所有能够赋值给Union的属性,将这些属性构成一个新的类型

Constructs a type by extracting from Type all union members that are assignable to Union.

Extract<Type, Union>

看如下例子:

type T0 = Extract<"a" | "b" | "c", "a" | "f">;

因为a assignable to a | f,其他的都不行,所以T0='a'

TypeScript 源码:

type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;

type T0 = Extract<"a" | "b" | "c", "a" | "f">;应该得到T0 = "a" | "b" | "c"才对,是一个一荣俱荣,一损俱损的状态,a是怎么被单独Extract的呢?

其实答案在extends中,extends为我们执行了分配律

type T0 = Extract<"a" | "b" | "c", "a" | "f">
= "a" extends "a"|"f" ? never : "a" | //"a"
"b" extends "a"|"f" ? never : "b" | //never
"c" extends "a"|"f" ? never : "c" //never
= "a"

2.2、 Exclude

同样的规则,可以理解下 Exclude

type T0 = Exclude<"a" | "b" | "c", "a" | "f">
= "a" extends "a"|"f" ? never : "a" | //never
"b" extends "a"|"f" ? never : "b" | //"b"
"c" extends "a"|"f" ? never : "c" //"c"
= "b"|"c"

2.3、 Pick

pick 是很简单的,看一个例子就可以理解。

type Test = {
name: string;
age: number;
salary?: number;
};
type omitted = Omit<Test, "age">;

2.4、 Omit

Omit相对而言复杂一些

先对比下二者的区别

type A4 = Pick<'a' | 'b', 'a'>;
// error: a 不属于 keof 'a' | 'b'

type A5 = Omit<'a' | 'b', 'a'>;
// 不报错

那么为什么omit不报错呢?我们看看源码

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

K extends any你写啥都不报错。

然后我们逐步拆解下Omit的每一步做了什么

type T = "a" | "b";
type omitted = Omit<T, "a">;
//最关键
type keyofString = keyof T;
type excludedKeyOfString = Exclude<keyofString, "a">;
type final = {
[P in excludedKeyOfString]: T[P];
};

最关键的步骤是这一行 type keyofString = keyof T这一步我们得到了一个联合类型,类型大致如下:

type A3 = number | typeof Symbol.iterator | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | ... 36 more ... | "b"

所以上面说的不止看着像,实际上就是一个字符串的属性和方法。 那字符串方法上有没有a属性呢?显然没有。所以 Exclude<keyof T, K> 没用。 最后,Pick<T, Exclude<keyof T, K>>,T="a"|"b"自然具有字符串的所有属性,也因此Pick把所有的属性都选了出来,Pick了个寂寞。


3、 实现 Pick 函数和类型

pick.ts
type Pick2<T, K extends keyof T> = {
[P in K]: T[P];
}

const obj = {
name: 'test',
age: 10,
city: 'wuhan',
};

function Pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick2<T, K> {
const picked: Partial<T> = {};
keys.forEach((key) => {
if (obj.hasOwnProperty(key)) {
picked[key] = obj[key];
}
});

return picked as Pick2<T, K>;
}

console.log(Pick(obj, ['name', 'city']));

4、 断言

类型断言(Type Assertion)是 TypeScript 中的一种机制,允许开发者在代码中“断言”某个值的类型,告诉编译器此处的值是什么类型。

TypeScript 一旦发现存在类型断言,就不再对该值进行类型推断,而是直接采用断言给出的类型。

类型断言在 TypeScript 中的作用主要体现在以下几个方面:

  • 绕过类型检查:在某些情况下,TypeScript 的类型检查机制可能会过于严格,导致一些本来合法的代码无法编译通过。此时,可以使用类型断言来绕过类型检查,让代码能够正常编译通过。
  • 强制类型转换:类型断言可以用来强制将一个值转换为另一个类型。这在某些情况下非常有用,例如,当我们需要将一个字符串转换为数字时。
  • 提高代码可读性:类型断言可以帮助提高代码的可读性,使代码更容易理解。 类型断言的语法

在 TypeScript 中,类型断言有两种语法形式:

  • 尖括号语法:<类型>变量名
  • as 语法:变量名 as 类型

示例

// 尖括号语法
const element = document.getElementById('foo');
const inputElement = <HTMLInputElement>element;

// as 语法
const element = document.getElementById('foo');
const inputElement = element as HTMLInputElement;

在上面的示例中,我们使用类型断言将 element 变量断言为 HTMLInputElement 类型。这告诉编译器,element 变量是一个 HTMLInputElement 实例,我们可以对其使用 HTMLInputElement 类型的所有属性和方法。