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 函数和类型
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 类型的所有属性和方法。