TS-03 泛型、类
一、泛型
泛型的简单使用
泛型就相当于是一个变量
// 使用T来约束类型, 后面传入的什么, 类型就是什么, 如果传入number, 则返回的是以number构成的数组
// T不是固定写法,一般使用T;
const getArray = <T>(value: T, times: number = 5): T[] => {
return new Array(times).fill(value)
}
// 调用之前指定类型
// 如果传入string, 则会报错, string是没有length的;
getArray<number>(123, 4).map(item => item.length)
给泛型传入变量使用<>来包裹
多个泛型定义
当一个变量不够用时,就需要使用多个变量了
const getArray = <T, U>(value: T, params: U, times: number): Array<[T, U]> => {
return new Array(times).fill([value, param2])
}
// 也可以不指定类型, 会根据值自动确定类型;
getArray(1,'a',3).forEach(item => {
console.log(item[0].length) // 报错, 数字类型没有length属性
})
接口使用泛型变量
// 第一种
interface getArr {
<T>(arg1: T, times: number): T[]
}
// 第二种
interface getArr<T> { // 使用这种形式, 里面定义的都可以使用该变量了
(arg1: T, times: number): T[],
array: T[] // 以t构成的数组;
}
泛型约束
- 使用
extends
来约束类型,下面的例子表示传入的参数需要有length属性;(extends
关键字为继承接口,当接口被继承之后,接口有的东西继承的接口也需要有)
interface ValueWithLength {
length: number
}
const getArr = <T extends ValueWithLength>(arg1: T, times): T[] => {
return new Array(times).fill(arg)
}
getArr(123) // 报错
- 使用
extends keyof
泛型约束限制值是对象里面的一员,keyof
是连接符,将对象里的所有属性名连接成一个联合类型,后面高阶类型中有提到;
let obj = {
a: 'a',
b: 'b'
}
// extends keyof表示继承T,相当于obj返回的键名组成的数组, k就是其中的一员
const getProps = <T, K extends keyof T>(object: T, propName: K) => {
return object[propName]
}
getProps(obj, 'a')
getProps(obj, 'c') // 报错, (如果不进行keyof约束, 则不会报错, 返回undefined)
泛型中的默认值
function createArr<T = string>(params: T): Array<T> {
return new Array(params)
}
二、TS中的类
定义类
定义类时,需要在类的顶部初始化实例属性,之后才能在constructor里接收参数进行赋值,否则报错;
class Point {
public x: number
public y: number
constructor(x: number, y:number){
this.x = x
this.y = y
}
public getPosition(){
return `(${this.x}, ${this.y})`
}
}
类的修饰符
public
公共的private
私有的- 外部访问定义private的属性将报错,只能在类中的访问
- 继承也无法访问私有的属性的
protected
受保护的与private的区别是继承的子类可以访问
给constructor添加protected只能通过子类继承创建实例,自身不能创建实例
tsclass Parent { protected age: number protected constructor(age: number){ this.age = age } } class Child extends Parent { constructor(age: number) { super(age) } } let p = new Parent(18) // 报错 let c = new Child(18) // 正常
私有属性
在ts的类中,使用private
表示私有的属性,ts3.8中可以使用#表示私有字段
class Persion {
private name: string;
// or
#name: string;
}
需要注意的是,private声明的属性可以通过Persion['name']获取,而使用#声明的,不能使用Persion['#name']获取
其他修饰符
只读属性:readonly
定义属性不可被更改
使用readonly也是需要添加前三个修饰符的;
tsclass UserInfo { public readonly name: string constructor(name: string) { this.name = name } }
静态属性:static
标识静态属性,静态属性只能通过类访问,不能通过实例访问
class Parent {
public static age: number = 18
public static getAge(){
return Parent.age
}
constructor(){}
}
console.log(Parent.getAge()) // 18
参数属性
简化定义类需要初始化属性的过程
class A {
constructor(public name: string){}
}
let a = new A('heny') // {name: 'heny'}
在参数属性前添加修改符,会自动将属性挂载到实例上面,不需要在里面定义了;
可选类属性
- 使用问号定义可选属性
class Info {
public name: string
public age?: number
constructor(name: string, age?: number, public sex: string){
this.name = name
this.age = age
}
}
类的存取器
调用值时访问的函数,和取值时调用的函数
- 与es6中的get、set没有区别
- 存取器不需要修饰符
class Info {
private _info: string
get infoStr(){
return this._info
}
set infoStr(val){
this._info = val
}
}
let info = new Info()
// 初始化info
info._info = 'hahaha'
console.log(info.infoStr)
抽象类
使用
abstract
来定义抽象类定义抽象类只能继承使用,不能直接实例化
在抽象类里面定义的抽象属性或方法,在继承时需要添加到继承的类中;
abstract class People {
constructor(public name: string){}
public abstract printName(): void // 抽象方法只需要定义方法名和接收的参数以及返回值即可
}
class Man extends People {
constructor(name: string){
super(name)
this.name = name
}
public printName(){ // 需要手动实现该方法printName
console.log('hahaha')
}
}
- 使用抽象类定义存取器
abstract class People {
public abstract _name: string
abstract get insideName(): string
abstract set insideName(val: string) // set不能标记返回值
}
注意:抽象方法和抽象存取器都不能包含实际的代码块,只需要标记属性名,方法参数,返回值类型,存值器函数不需要标记返回值;
实例类型
所有的类都是一个类型;
class People {
constructor(public name: string){}
}
let p1: People = new People('hny') // p1可以手动指定实例类型,也是可以省略的,会自动根据后面的类型进行推断;
类实现接口
- 使用
implements
实现一个接口
interface Foold {
type: string
}
class Fool implements Foold {
public type: string
}
注意:接口检测的是该接口定义的类创建的实例,如果定义的类里面的属性不是public,而是static,则会报错,提示丢失属性
接口继承类
当接口继承类时,会继承这个类的成员,不包括去实现,也就是说,接口也会继承类的private
和protected
修饰符的成员,因为这两个成员是需要实现的,而接口继承类不需要去实现,只会被它的子类实现
class A {
protected name: string
}
interface I extends A{}
class B extends A implements I {
// 由于继承的A定义了protected属性需要使用实例才能访问,因此需要同时继承A再实现I接口
public name: string
}
泛型中使用类 类型
例子:
const create = <T>(c: new() => T): T => {
return new c()
}
class Info {
public name: string
constructor(){
this.name = 'h'
}
}
create(Info)
在上面的例子中,传入了一个类,返回的是一个类创建的实例,参数c的类型是new()
,代表的是调用这个类的构造函数,类型就是类创建实例后的类型;