一、泛型语法的基本使用
1. 认识泛型
- 软件工程的主要目的是构建不仅仅明确和一致的API,还要让代码具有很强的可重用性
- 如可以通过函数来封装一些API,通过传入不同的函数参数,让函数帮助我们完成不同的操作
- 但是对于参数的类型是否也可以参数化呢?
- 什么事类型的参数化
- 使用TypeScript,考虑这个参数和返回值的类型需要一致,如下的写法不适用于其它类型,如string、boolean、Person等
function func(arg: number): number {
return arg
}
2. 泛型使用
- 虽然any是可以的,但是定义为any的时候,其实已经丢失了类型信息
- 如传入一个number,希望返回的不是any类型,而是number类型
- 所以,需要在函数中可以捕获到参数的类型是number,并且同时使用它作为返回值的类型
- 需要使用
类型变量(type variable),它作用于类型,而不是值
function func<T>(arg: T): T {
return arg
}
- 这里可以使用两种方式来调用它
- 通过 <> 的方式将类型传递给函数
- 通过类型推导(type argument inference),自动推导出我们传入变量的类型
- 这里会推导出它们是字面量类型的,因为字面量类型对于我们的函数也是适用的
func<string>('aaa')
func<number>(111)
func('bbb')
func(222)
3. useState封装
function useState<T>(initialState: T): [T, (newState: T) => void] {
let state = initialState
function setState(newState: T) {
state = newState
}
return [state, setState]
}
const [count, setCount] = useState(1)
const [msg, setMsg] = useState('Hello World')
const [banners, setBanners] = useState<any[]>([])
4. 泛型的基本补充
function func<T, E>(x: T, y: E) {}
- 平时在开发中可能会遇到的常用名称
- T:Type的缩写,类型
- K、V:key和value的缩写,键值对
- E:Element的缩写,元素
- O:Object的缩写,对象
二、泛型接口、类的使用
1. 泛型接口
interface IFunc<T> {
initialValue: T,
valueList: T[],
handleValue: (value: T) => void
}
const func: IFunc<number> = {
initialValue: 0,
valueList: [0, 1, 3],
handleValue: function(value: number) {
console.log(value)
}
}
interface IFunc<T = number> {
initialValue: T,
valueList: T[],
handleValue: (value: T) => void
}
2. 泛型类
class Point<T> {
x: T
y: T
constructor(x: T, y: T) {
this.x = x
this.y = y
}
}
const p1 = new Point(1, 2)
const p2 = new Point<number>(2, 3)
const p3: Point<number> = new Point(3, 4)
三、泛型约束和类型条件
1. 泛型约束(Generic Constraints)
- 有时候希望传入的类型有某些共性,但这些共性不在同一个类型中
- 如 string 和 array 都是有 length的,或某些对象也可以有 length 属性
- 那么只要是拥有 length 的属性都可以作为参数类型
interface ILength {
length: number
}
function getLength<T extends ILength>(args: T) {
return args.length
}
console.log(getLength("1"))
console.log(getLength(["1", "2"]))
console.log(getLength({length: 1, age: 17}))
- 这里表示传入的类型必须有这个属性,也可以有其他属性,必须有这个成员
2. 泛型约束使用
- 在泛型约束中使用类型参数(Using Type Parameters in Generic Constraints)
- 可以声明一个类型参数,这个类型参数被其它类型参数约束
- 希望获取一个对象给定属性名的值
- 需要确保不会获取 obj 上不存在的属性
- 在两个类型之间建立一个约束
- extends
- keyof 类型 => key 的联合类型
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
const obj = {
name: 'ikun',
age: 25,
}
interface Obj {
name: string
age: number
}
const name = getProperty<Obj>(obj, 'xx')
const name = getProperty<Obj, 'xx'>(obj, 'xx')
四、TypeScript映射类型
1. 映射类型(Mapped Types)
- 一个类型需要基于另外一个类型,拷贝一份又不优雅,此时可以考虑映射类型
- 大部分内置的工具都是通过映射类型来实现的
- 大多数类型体操的题目也是通过映射类型完成的
- 映射类型建立在索引签名的语法上
- 映射类型,就是使用了 PropertyKeys 联合类型的泛型
- 其中, PropertyKeys 多是通过 keyof 创建,然后循环遍历键名创建一个类型
interface IPerson {
name: string
age: number
}
type MapType<T> = {
[k in keyof T]: T[k]
}
type NewPerson = MapType<IPerson>
2. 映射修饰符(Mapping Modifiers)
- 在使用映射类型时,有两个额外的修饰符可能会用到
- readonly,用于设置属性只读
- ?,用于设置属性可选
- 也可以通过前缀 - 或+ 删除或者添加这些修饰符,如果没写前缀,相当于使用了 + 前缀
type MapType<T> = {
-readonly [k in keyof T]-?: T[k]
}
interface IPerson {
readonly name: string
age?: number
height: number
}
type NewPerson = MapType<IPerson>
五、TypeScript条件类型
1. 条件类型(Conditional Types)
- 开发中需要基于输入的值来决定输出的值,同样也需要基于输入的值类型来决定输出的值的类型
- 条件类型(Conditional types)用来描述输入类型和输出类型之间的关系
- 条件类型的写法类似 JavaScript 中的条件表达式(condition ? trueExpression : falseExpression)
- SomeType extends otherType ? TrueType : FalseType
function sum<T extends number | string>(arg1: T, arg2: T): T extends string ? string : number
function sum(arg1: any, arg2: any) {
return arg1 + arg2
}
const res1 = sum(1, 2)
const res2 = sum('a', 'b')
const res3 = sum('a', 1)
2. 在条件类型中推断(infer)
- 在条件类型中推断
- 条件类型提供了 infer 关键词,可以从正在比较的类型中推断类型,然后在 true 分支里引用该推断结果
- 现有一个函数类型,想要获取到一个函数的参数类型和返回值类型
type CalcFnType = (num: number, str: string) => number
type GetReturn<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never
type GetReturnType = GetReturn<CalcFnType>
type GetParams<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never
type GetParamsType = GetParams<CalcFnType>
3. 分发条件类型(Distributive Conditional Types)
- 在泛型中使用条件类型的时候,如果传入一个联合类型,会变成分发的(distributive)
type ToArray<T> = T extends any ? T[] : never
type ToArray<T> = T[]
type newType = ToArray<string | number>
- 在 ToArray 传入一个联合类型,这个条件类型会被应用到联合类型的每个成员
- 当传入 string | number 时,会遍历联合类型中的每一个成员
- 相当于 ToArray<string> | ToArray<number>
- 最后的结果是:string[] | number []
六、类型工具和类型体操
1. 内置工具和类型体操介绍
- 类型系统其实在很多语言中都是有的,如Java、Swift、C++等,但相对来说 ts 的类型更灵活
- 这是因为 typescript 的目的是为 JavaScript 添加一套类型校验系统,因为 JavaScript 本身的灵活性,让 typescript 类型系统不得不增加更附加的功能以适配 JavaScript 的灵活性
- 所以 typescript 是一种可以支持类型编程的类型系统
- 这种类型编程系统为 typescript 增加了很大的灵活度,同时也增加了它的难度
- 业务开发基本不需要太多的类型编程能力
- 开发框架、库、或通用性工具,为了考虑各种适配情况,就需要使用类型编程
- typescript 本身提供了类型工具,可以辅助进行类型转换(之前有关于this的类型工具)
- 类型体操题目