# 巩固 TypeScript 基础

# 泛型
# T 是什么意思
- T(Type)用来表示类型,在泛型中用来表示类型的范围
# 泛型是什么
- 其实可以理解为声明一个类型,但是不赋最终的类型,类似于 js 中的
var a;
- 且泛型更多的是可以用来帮我们约束类型的范围
- 在泛型中也可以通过
extends
继承
function studen<T, U>(age: T, name: U): T {
return age;
}
//把泛型当作参数,调用时传入泛型的类型值
studen<number, string>(16, "zhoukingzz");
//也可以不给泛型传值,通过实际参数让ts自己推导泛型值
studen(16, "zhoukingzz");
//通过继承获取泛型的类型约束
type nameType = string | number;
interface student<T extends nameType> {
name: T;
age: number;
}
# type 和 interface 的区别
type
:类型别名。我们可以用它来定义一个基本类型,也可以用它来定义联合类型。同时 type 支持泛型。起别名不会创建一个新类型。interface
:只能用来定义对象类型。在定义接口的时候,是可以同时声明对象上的属性和方法的。
# 相同点
type
和interface
都可以用来描述对象或者函数。举个栗子:
type student = {
age: number;
name: string;
};
interface student {
age: number;
name: string;
}
type joinClass = (age: number, name: string) => void;
interface joinClass {
(age: number, name: string): void;
}
# 不同点
type
和interface
有各自不同的扩展方式
//type通过交叉运算符&扩展
type student = {
age: number;
name: string;
};
type moreInfo = student & {
gender: boolean;
};
//inetrface 通过extends继承拓展
interface student {
age: number;
name: string;
}
interface moreInfo extends student {
gender: boolean;
}
- 接受类型不同
type
可以接收基本类型、函数类型、映射类型、联合类型和元组类型interface
接受对象类型
- 且同名接口会自动合并,而类型别名不会
# ts 中枚举和常量枚举的区别
- 在 ts 中除了有数字枚举和字符串枚举外,还有常量枚举。
- 它是使用 const 关键字修饰的枚举,常量枚举使用内联语法,不会为枚举类型生成任何 JS,它们在编译阶段会被删除 (ps 还有异构枚举)
# ts 中 Tuple 元组是什么
- 数组一般有同种类型的值组成,但是我们需要在数组中存储不同类型的值,这是好我们可以使用元组。元组最重要的特性是可以限制数组元素的个数和类型
let x: [string, number];
x = ["bajiu", 10]; // Ok
x = ["bajiu", 10, 10]; // Error
- 在定义元组类型是,我们也可以通过?来声明元组类型的可选元素
let optTuple = [string, boolean?];
optTuple = ['bajiu', true];
optTuple = ['bajiu'];
- 剩余元素 剩余元素代表元组类型是开放的,可以有零个或多个额外的元素
type RestTuple = [number, ...string[]];
let restTuple: RestTuple = [666, "123", "234"];
# unknown 和 any 的区别
- unknown 与 any 的最大区别是: 任何类型的值可以赋值给 any,同时 any 类型的值也可以赋值给任何类型。unknown 任何类型的值都可以赋值给它,但它只能赋值给 unknown 和 any
- unknown 必须缩小范围后,才能调用起对应的一些方法
# ts 中断言有几种语法?
- 尖括号语法
let str: unknown = "bajiu";
let strLen: number = (<string>str).length;
- as 语法
let str: unknown = "bajiu";
let strLen: number = (str as string).length;
- ! 非空断言运算符 x! 将从 x 值域中排除 null 和 undefined
- !. 在变量名后添加,可以断言排除 undefined 和 null 类型
# ts 中对象展开会有什么副作用吗?
- 仅包含对象自身的可枚举属性,不可枚举的属性将会丢失。
# ts 中 declare,declare global 是什么?
- declare 是用来定义全局变量、全局函数、全局命名空间、js modules、class 等
- declare global 为全局对象 window 增加新的属性
# 简述工具类型 Exclude、Omit、Merge、Intersection、Overwrite 的作用
- Exclude<T, U> 从 T 中排除出可分配给 U 的元素。
- Omit<T, K> 的作用是忽略 T 中的某些属性。
- Merge<O1, O2> 是将两个对象的属性合并。
- Compute<A & B> 是将交叉类型合并
- Intersection<T, U>的作用是取 T 的属性,此属性同样也存在与 U。
- Overwrite<T, U> 是用 U 的属性覆盖 T 的相同属性。
# 索引类型查询操作符 keyof
- keyof 关键字是将一个类型映射为它所有成员名称的联合类型
- 注意是类型而不是具体值,所以 keyof 可以用于 interface 接口,type 类型或者 class 类。返回的是一个联合类型。
interface Person {
name: string;
age: number;
location: string;
}
// 返回Person对象成员名称
type K1 = keyof Person; // "name" | "age" | "location"
// 返回数组对象成员名称
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ...
// 返回{ [x: string]: Person }对象成员名称, 也就是string
type K3 = keyof { [x: string]: Person }; // string
# 索引访问操作符 T[K]
- 获取成员对应的属性值的类型(按上面的 Person 的话,需求就是把 Person 的属性值类型全部获取出来 string | number)。
interface Person {
name: string;
age: number;
location: string;
}
// 返回Person对象成员名称
type K1 = keyof Person; // name" | "age" | "location
// 获取Person成员对应属性值的类型
type kValue = Person[K1]; // string | number
//使用索引访问操作符 T[K]实现lodash的pick方法(即获取某个对象的某些值)
const app = {
name: "app",
id: 0,
show: false,
};
const res = pick(app, ["name", "id"]); //[app,0]
//用ts实现如下
function mypick<T, k extends keyof T>(obj: T, names: k[]): t[k][] {
return names.map((name) => obj[name]);
}
# 映射类型 in
# 映射类型的语法是[k in keys]
k
是类型变量,依次绑定到每个属性上,对应每一个属性名的类型keys
是联合类型,一组字符串属性名类型(type app = 'name' | 'id')
- 假设我们需要把一个对象 app 的属性全部转化为可选参数
?.
,这个时候就需要用到映射类型 - 在 ts 中,获取具体属性值类型可以通过
索引访问操作符T[K]
,把原有的参数转化为可选参数是[K in Keys]:T[k]
,其中 Keys 的属性名联合类型可以通过索引类型查询操作符 keyof T
。最终实现[K in keyof T]?:T[k]
具体操作如下:
//1. 通过'索引类型查询操作符 keyof T'获取app的属性名联合类型keyof app
const app = {
name: "app",
id: 0,
lv: "99+",
};
type Keys = keyof app; // 'name' | 'id' | 'lv'
//2. 通过映射类型映射属性名联合类型[K in Keys],这一步也就是转化的key
//3. 获取转化的value,通过obj[key]的方法获取value。也就是app[k]
//4. 最终返回的新类型就是{[K in keyof app]: app[K]}
//5. app类型可以通过泛型T来代替`type myPartial<T> = {[K in keyof T] ?: T[K] }`
// 具体实现
interface element {
name: string;
age: number;
location: string;
}
type myPartial<T> = { [K in keyof T]?: T[K] };
type ElementPartial = myPartial<element>; //ElementPartial的ts提示如下
//type ElementPartial = {
// name?: string | undefined;
// age?: number | undefined;
// location?: string | undefined;
//}
- 同理,内置工具类型 Pick,Readonly, Required 也可以实现。
interface element {
name: string;
age: number;
location: string;
}
// Readonly用于将成员所有属性置为readonly,无法再次赋值
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };
type PersonReadOnly = MyReadonly<Person4>; //PersonReadOnly的ts提示如下
/*
type PersonReadOnly = {
readonly name: string;
readonly age: number;
readonly location: string;
}
*/
// Required用于将成员所有属性置为必填参数,与Partia相反
type MyRequired<T> = { [K in keyof T]-?: T[K] };
type PersonRequired = MyRequired<Person4>;
// Pick接受两个泛型T,U 从T类型中挑选出U类型的属性生成一个新的类型,而且泛型U必须是T类型的字符串联合类型的子集
type MyPick<T, U extends keyof T> = { [P in U]: T[P] };
// 泛型约束的关系只能传入 name age location的联合类型
type PersonPick = MyPick<Person4, "name" | "age">; //作用:生成一个新类型,该类型拥有 T 中的 K 属性
/*
type PersonPick = {
name: string;
age: number;
}
*/