关于对象类型和函数类型之类的,还有一些补充。
函数可是设置可选参数
// 可选参数 ?
function css1(el: HTMLElement, attr: string, val?: any) {
}
let div = document.querySelector('div');
div && css1(div, 'width', '100px');
div && css1(div, 'color');
可以通过联合类型和默认值来设置函数默认参数
// 默认参数
function sort1(items: Array<number>, order = "desc") {
}
sort1([1, 2, 3]); // order不填 默认 desc
// 也可以通过联合类型来限制取值
function sort2(items: Array<number>, order: "desc" | 'asec' = "desc") {
}
sort2([1, 2, 3]); // order不填 默认 desc
sort2([1, 2, 3], "asec");
// sort2([1, 2, 3], "abc"); // error
函数定义剩余参数
如果函数的参数有一部分参数是相同的类型标注,则可以通过interface进行统一标注
// 剩余参数
interface IObj {
[key: string]: any;
};
function merge(target: IObj, ...others: Array<IObj>) {
return Object.assign(target, ...others);
}
let newObj = merge({x: 1}, {y: 2}, {z: 3});
利用 类型断言 解决this指向问题
普通函数中的this是谁调用的话就会指向谁,普通函数中的`this`一般为any会报错。
interface T {
a: number;
fn: (x: number) => void;
};
// 在第一个参数位显示的标注this 解决this指向问题
let tf_obj1: T = {
a: 1,
fn(this: T,x: number) {
}
};
箭头函数中的this是固定的,this指向当前函数的作用域环境。
interface T {
a: number;
fn: (x: number) => void;
};
let ft_obj2: T = {
a: 1,
fn(this: T, x: number) {
return () => {
// 不能动态的改变箭头函数中的this
// fn中的this指向什么,内部的箭头函数中的this就指向什么
}
}
};
函数重载
一个函数有时候会传入不同的参数,不同的参数类型输入会有相对应的不同的返回结果,所以就需要用到函数重载。
重载函数不需要实体,只需要定义其结构(类似于接口)。
interface PlanObject {
[key: string]: string | number;
}
function czcss(ele: HTMLElement, attr: PlanObject);
function czcss(ele: HTMLElement, attr: string, val: string | number);
function czcss(ele: HTMLElement, attr: any, val?: any) {
if(typeof attr === "string" && val) {
ele.style[attr] = val;
}
if(typeof attr === "object") {
for(let key in attr) {
ele.style[key] = attr[key];
}
}
}
if(div1) {
czcss(div1, 'width', '100px');
czcss(div1, {
height: '100px',
});
// czcss(div1, 'opacity'); // error
}
类
类的定义
class User {
/**
*
* @param id
* @param username
* 当我们给构造函数参数设置了访问修饰符:public,ts会做如下两件事情
* - 给当前类添加同名的成员属性
* - 在类的实例化的时候,会把传入的参数值赋值给对应的成员属性
*/
constructor(
public id: number,
public username: string,
) {
// 创建类的函数,当类通过new实例化的时候,就会执行该函数
console.log('this is constructor');
// this.id = id;
// this.username = username;
};
// 方法
postArticle(title: string, content: string) {
console.log(`${this.username}发表了一篇文章${title},内容为${content}`)
};
}
let user1: User = new User(1, '1'); // use
user1.postArticle('title', 'content');
类的继承
当子类继承父类时,需要通过super调用父类的构造函数,且this必须在super之后使用。
class VIP extends User {
constructor(
id: number,
username: string,
public score: number,
) {
super(id, username); // 调用父类构造函数
console.log(this.id); // this必须在super之后使用
console.log('vip');
};
}
类中的方法也可以通过重写,重新实现父类的方法,但是需要保持参数的一致。
postArticle(title: string, content: string): void {
this.score++;
console.log(`${this.username}发表了一篇文章${title},内容为${content},分数${this.score}`);
};
类中也可以实现方法重载。
postArticle(title: string, content: string);
postArticle(title: string, content: string, file: string);
postArticle(title: string, content: string, file?: string): void {
super.postArticle(title, content);
if(file) {
this.postAttachment(file);
}
}
postAttachment(file: string) {
console.log(`${this.username}上传了${file}`)
};
let vip = new VIP(2, '2', 22);
vip.postArticle('222', '234');
vip.postAttachment('1.png');
vip.postArticle('333', '345', '2.png');
类的修饰符
类还具有一些修饰符:public protected private readonly。
- readonly 可以访问 但是一旦确定不能修改
- protected 可以访问 但是不能外部修改
- private 外部包括子类都不能访问 也不可以修改
类还具有寄存器set | get,类似于defineObject的getter | setter。
set password(password: string) {
if(password.length >= 6) {
this._password = password;
}
}
get password(): string {
return '****';
}
类的静态成员 static
如果一个成员方法中没有使用或者是依赖this,那么该方法就是静态的。
类的静态成员 是通过 类名.静态成员访问的。
class User {
// static 必须在 readonly 之前
static readonly ALLOW_IMG_TYPE_LIST: Array<IAllowFileTypeList> =
['png' , 'gif' , 'jpg' , 'jpeg' , 'webp'];
constructor(
public id: number,
) {};
static info() {
console.log(User.ALLOW_IMG_TYPE_LIST);
}
}
console.log(User.ALLOW_IMG_TYPE_LIST);
抽象类 abstract
子类继承了抽象类,那么该子类就必须实现抽象类中的所有抽象方法,否则该类还得声明为抽象的。
// Component不能实例化
abstract class Component<T1, T2> {
props: T1;
state: T2;
constructor(props: T1) {
this.props = props;
};
// render(): string {
// return '';
// };
abstract render(): string;
}
// 可以用于规范化类的设计规范
interface IMyComponentProps {
val: number;
}
interface IMyComponentState {
x: number;
}
/**
* 类和接口
* 接口 使用 implements
*/
// 抽象类编译后还是会产生实体代码,但是接口不会
// 一个子类只能有一个父类,但是一个类可以实现多个接口
// 接口不能有实现,抽象类可以
interface ILog {
getInfo(): string;
}
// 接口也是可以继承的
interface IStrong extends ILog {
// 有 getInfo 和 save
save(data: string): void;
}
class MyComponent extends Component<IMyComponentProps, IMyComponentState>
implements ILog {
constructor(props: IMyComponentProps) {
super(props);
this.state = {
x: 1,
};
};
render(): string {
this.props.val;
this.state.x;
return '<myComponent />';
};
getInfo(): string {
return `<mycomponent> props ${this.props} state ${this.state}`;
}
}
let mycomponent = new MyComponent({val: 1});
mycomponent.render();
function log(target) {
target.getInfo();
}
log(mycomponent);
类型保护
类型保护,类似于类型断言,根据判断逻辑的结果,缩小类型的范围(精确类型)。
一般使用逻辑条件语块 if else elseif 或者一些关键词 typeof instanceof in is... 来实现。
Tips
in 内部使用for...in对类型进行遍历(in后面的类型必须是string | number | Sumbol)
获取类型的key - keyof
keyof可以获取类型的所有key的集合。
类型兼容 implements
implements可以实现一些类共有方法或者属性的提取。
interface IFly {
fly(): void;
}
class Person implements IFly {
name: string;
age: number;
study() {};
fly(): void {
};
}
class Cat {
name: string;
age: number;
catchMouse() {};
}
let p = new Person();
let c = new Cat();
function fn(arg: IFly) { // 接口 兼容
arg.fly();
}
fn( p ); // c没有fly