# 巩固 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:只能用来定义对象类型。在定义接口的时候,是可以同时声明对象上的属性和方法的。

# 相同点

  • typeinterface都可以用来描述对象或者函数。举个栗子:
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;
}

# 不同点

  1. typeinterface有各自不同的扩展方式
//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;
}
  1. 接受类型不同
  • type可以接收基本类型、函数类型、映射类型、联合类型和元组类型
  • interface接受对象类型
  1. 且同名接口会自动合并,而类型别名不会

# 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]

  1. k是类型变量,依次绑定到每个属性上,对应每一个属性名的类型
  2. 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;
}
*/
Last Updated: 3/23/2023, 2:54:16 AM