TypeScript
类型注解:是一种轻量级的为函数或变量添加的约束
TS提供了静态代码分析,能够在编译期发现代码写的不规范的地方。
TS能够支持所有JS支持的类型,此外它还能支持枚举,元组等类型
//数组类型
//数组定义方式1
// 语法; let 变量名:数据类型[] = [值1,值2,值3]
let arr1:number[] = [10,20,30,40,50]
//数组定义方式2: 泛型的写法
//语法: let 变量名: Array<数据类型> = [值1,值2,值3]
let srr2 = Array<number> = [12, 13, 14]
//注意问题:数组定义之后,里面的数据类型必须和定义时候的数据类型是一致的,否则有错误提示信息,也不能编译通过
// =====================
//元组类型:在定义数组的时候,类型和数据的个数在一开始就已经确定了
let arr3: [string, number, boolean] = ['', 13, true];
//注意问题:元组类型在使用的时候,数据类型的位置和数据个数应该和定义时候的数据类型和个数一致
//========================
//枚举类型, 枚举里面的每个数据都可以叫元素
enum Color {
red, green, blue
}
//定义一个Color的枚举类型的变量来接收枚举的值
let color:Color = Color.red;
console.log(color)
console.log(Color.red, Color.green, Color.blue)
console.log(Color[3])
void类型,当一个函数没有返回值的时候,指定为void类型
//定义void类型的变量,可以接收一个undefined的值,但是意义不大
let vd:void = undefined;
console.log(vd)
联合类型 |
一般用于函数参数可能是多种类型 function F(ss: string | number){}
类型断言: 告诉编译器,我知道自己是什么类型,也知道自己在干什么
类型断言的语法1:<类型>变量名 str
类型断言的语法2: 变量名 as 类型 str as string
类型推断
TS会在没有明确指定类型的时候推测出一个类型
复习
接口对象类型
接口是对象的状态(属性)和行为(方法)的抽象(描述)
接口是一种类型,是一种规则,是一种约束
interface IPerson {
name:string,
age:number
}
function Person(p:IPerson){
return {
name:p.name,
age: p.age
}
}
接口函数类型
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。
它就像是一个只有参数列表和返回值类型的函数定义,参数列表里面的每个参数都需要名字和类型
(()=>{
//函数类型:通过接口的方式作为函数类型来使用
//定义一个接口,用来作为某个函数的类型使用
interface ISearchFunc {
//定义一个调用签名
(source:string, subString: string): boolean
}
//定义一个函数,该类型就是上面定义的接口
const searchString: ISearchFunc = function(soure:string, subString:string){
return source.search(subString) > -1
}
//调用函数
console.log(searchString('哈哈,环境', ‘哈’))
})()
类类型:类的类型可以通过接口来实现
(()=>{
//定义一个接口
interface IFly{
//该方法没有任何的实现(方法中什么都没有)
fly()
}
//定义一个类,这个类的类型就是上面定义的接口(实际上也可以理解为,IFly这个接口约束了当前这个Person类)
class Person implements IFly {
//实现接口中的方法
fly(){
console.log('i can fly, i'm superman')
}
}
//实例化对象
const person = new Person()
person.fly()
})()
//定义一个接口
interface ISwim{
swim()
}
//定义一个类,同时实现IFly和ISwim两个接口
class Person2 implements IFly,ISwim{
fly(){
}
swim(){
}
}
//实例化对象
const person2 = new Person2()
person2.fly()
person2.swim();
//总结: 类可以通过接口的方式,来定义这个类的类型
//类可以实现一个接口,类也可以实现多个接口,要注意,接口中的内容都要真正的实现
//接口可以继承其他的多个接口
interface IMyFlyAndSwim extends IFly, ISwim {
}
class Person3 implements IMyFlyAndSwim {
fly(){}
swim(){}
}
const person3 = new Person3();
person3.fly()
person3.swim()
// 总结: 接口和接口之间叫继承(使用的是extends关键字),类和接口之间叫实现(使用的是implements关键字)
类:可以理解为模版,通过模版实例化对象
//面向对象的编程思想
(()=>{
class Person {
name:string,
age:number,
gender: string
constructor(name: string='xiaohua', age: number=16, gender: string='female'){
this.name = name
this.age = age
this.gender = gender
}
//定义实例方法
sayHi(str: string){
console.log('hello' + str);
}
}
const person = new Person('zuozuo', 1, 'male')
person.sayHi('what's your name?')
})()
继承:类与类之间的关系
继承后类与类之间的叫法:
A类继承了B这个类,那么此时A类叫子类,B类叫基类
子类 -----》 派生类
基类-----〉超类(父类)
一旦发生了继承的关系,就出现了父子类的关系
(()=>{
class Person{
name:string,
age:number,
gender:string
constructor(name:string, age:number, gender:string){
this.name = name;
this.age = age;
this.gender = gender
}
sayHi(str:string){
console.log(this.name + str)
}
}
class Student entends Person {
constructor(name:string, age: number, gender: string){
//调用父类中的构造函数,使用的是super
super(name, age, gender)
}
//可以调用父类中的方法
sayHi(){
//调用父类中的sayHi方法
super.sayHi('haha')
}
}
//实例化Person
const person = new Person('daming', 4, 'nan')
person.sayHi('gaga')
const stu = new Student('xiaoming', 3, 'nv')
stu.sayHi()
//总结: 类和类之间如果要有继承关系,使用的是extends关键字
//子类可以调用父类的构造函数,使用的是super关键字(包括调用父类实例上的方法)
//子类可以重写父类的方法
})()
多态:父类型的引用指向子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为
(()=>{
//定义一个父类
class Animal {
//定义一个属性
name:string
//定义一个构造函数
constructor(name: string){
//更新属性值
this.name = name
}
//实例方法
run(distance: number = 0){
console.log(`distance is ${distance}`)
}
}
//定义一个子类
class Dog extends Animal{
//构造函数
constructor(name:string){
//调用父类的构造方法,实现了子类中属性的初始化操作
super(name)
}
run(distance:number){
console.log('distance is ' + distance)
}
}
//定义一个子类
class Pig extends Animal{
//构造函数
constructor(name:string){
//调用父类的构造方法,实现了子类中属性的初始化操作
super(name)
}
run(distance:number){
console.log('distance is ' + distance)
}
}
// 实例化父类对象
const ani:Animal = new Animal('animal')
ani.run()
//实例化子类对象
const dog: Dog = new Dog('dog')
dog.run()
const dog: Animal = new Dog('dahuang')
dog.run()
const pig: Animal = new Pig('xiaozhu')
pig.run();
})()
修饰符(类中的成员的修饰符)
主要是描述类中的成员(属性,构造函数,方法)的可访问性
// public修饰符,类中的成员的默认修饰符,代表是公共的,任何位置都可以访问类中的成员
//private修饰符,类中的成员如果使用private来修饰,那么外部是无法访问这个成员变量的,当然,子类中也是无法访问该成员变量的
//protected修饰符,类中的成员如果使用protected来修饰,那么外部是无法访问的,子类中是可以访问的
(()=>{
//定义一个类
class Person{
name:string
constructor(name:string){
this.name = name
}
public eat(){
console.log('this bone is dilicious', this.name)
}
}
//实例化对象
const per = new Person('huahu')
//类的外部可以访问类中的属性成员
console.log(per.name)
per.eat()
})()
readonly修饰符
readonly首先是一个关键字,
对类中的属性成员进行修饰,修饰符后,该属性成员,就不能在外部被随意的修改了
可以在构造函数中对readonly修饰的属性进行修改
(()=>{
class Person {
readonly name:string = ‘da tian tian’
constructor(name:string){
this.name = name
}
sayHi(){
console.log('kao la kao la', this.name)
//类中的普通方法,也是不能修改readonly修饰的成员属性值
this.name = 'da tian tain'
}
}
//实例化对象
const person = new Person('xiao tian tian')
console.log(person)
cosnole.log(person.name)
person.name = 'da tian tian' // 报错, 此时无法修改name,因为name属性是只读的
console.log(person.name)
})()
// 对构造函数中的属性用readonly进行修饰
class Person {
//构造函数
//构造函数中的name参数,一旦使用readonly进行修饰后,那么该name参数可以叫做参数属性
//构造函数中的name参数,一旦使用readonly进行修饰后,那么Person中就有了一个name的属性成员
//构造函数中的name参数,一旦使用readony进行修饰后,外部也是无法访问类中的name属性成员值的
constructor(readonly name:string = 'da tian tian'){
this.name = name
}
}
const person:Person = new Person('xiao tian tian')
console.log(person)
person.name = 'zuozuo'
console.log(person.name)
存取器
让我们可以有效的控制对 对象中成员的访问,通过getters和setters来进行操作
(()=>{
class Person{
//姓名的成员属性(外部可以访问,也可以修改)
firstName: string;
lastName: string;
constructor(firstName:string, lastName:string){
this.firstName = firstName;
this.lastName = lastName
}
//读取器,负责读取数据的
get fullName(){
return this.firstName + "_" + this.lastName
}
//设置器,负责设置数据的(修改)
set fullName(val){
let names = val.split('_')
this.firstName = names[0]
this.lastName = names[1]
}
}
//实例化对象
const person:Person = new Person('dongfang', 'bubai')
console.log(person)
//获取该属性成员属性
console.log(person.fullName)
//设置该属性的数据
person.fullName = 'zhugeng_kongming'
console.log(person.fullName)
})()
静态成员
在类中通过static修饰的属性或者方法,那么就是静态的属性及静态的方法,也称之为静态成员
静态成员在使用的时候是通过点(类.)这种语法来调用的
(()=>{
class Person {
// 类中默认有一个内置的name属性,所以此时会出现错误的提示信息
static name1: string
constructor(name:string){
this.name1 = name
}
static sayHi(){
console.log('ni hao')
}
}
//实例化对象
const person:Person = new Person('xiao tian tian')
//通过实例对象调用的属性(实例属性)
console.log(person.name1)
//通过实例对象调用的方法(实例方法)
person.sayHi()
//通过类名.静态属性的方式来设置该成员数据
console.log(Person.name1)
Person.sayHi()
})()
抽象类
包含抽象方法(抽象方法一般没有任何的具体内容实现),也可以包含实例方法,抽象类不能被实例化,为了让子类进行实例化并实现内部抽象的方法
(()=>{
abstract class Animal {
abstract name:string;
//报错,因为抽象方法不能有具体的实现
// abstract eat(){
// console.log();
// }
abstract eat():any
//实例方法
sayHi(){
console.log('hello');
}
}
//不能创建抽象类的实例
// const ani: Animal = new Animal();
//定义一个子类(派生类)Dog
class Dog extends Animal {
name = 'hehe'
//重新的实现抽象方法, 这个方法为当前Dog类的实例方法
eat() {
console.log('guizhe chi');
}
}
const dog:Dog = new Dog();
dog.eat()
//调用的是抽象类中的实例方法
dog.sayHi();
console.log(dog.name);
})()
函数
封装一些重复使用的代码,在需要的时候直接调用即可
(()=>{
//函数的完整写法
// add3 -----> 变量名-----》函数add3
// (x:number, y:number)=>number 当前这个函数的类型
//function(x:number, y:number):number{ return x + y } 相当于符合上面这个函数类型的值
const add3:(x:number, y:number)=>number = function(x:number, y:number):number{
return x + y
}
console.log(add3(1, 2));
})()
可选参数和默认参数
(()=>{
//可选参数
//默认参数
//定义一个函数:传入姓氏和名字,可以得到姓名(姓氏+名字=姓名)
//需求:1. 如果不传入任何内容,那么久返回默认的姓氏
// 2. 如果只传入姓氏,那么就返回姓氏
// 3. 如果传入姓氏和名字,那么就返回姓名
const getFullName = function(firstName:string = 'dongfang', lastName?:string):string{
//判断名字是否传入了
if(lastName){
return firstName + '_' + lastName
}else{
return firstName
}
}
//函数调用
//什么都不传
console.log(getFullName());
//只传入姓氏
console.log(getFullName('zhuge'));
//传入姓氏和名字
console.log(getFullName('zhuge', 'kongming'));
})()
剩余参数(rest参数)
剩余参数是放在函数声明的时候,所有的参数的最后
(()=>{
// ...args:string[] --->剩余的参数,放在了一个字符串的数组中,args里面
function showMsg(str:string, ...args:string[]){
console.log(str, args); //a [ 'c', 'v', 'f', 'r' ]
}
showMsg('a', 'c', 'v', 'f', 'r')
})()
函数重载
函数名字相同,函数的参数和个数不同
(()=>{
//定义一个函数
// 需求: 有一个add函数,它可以接收2个string类型的参数进行拼接,也可以接收2个number类型的参数进行相加
//函数重载声明
function add(x:string, y:string): string
function add(x:number, y:number): number
//函数声明
function add(x:string|number, y:string|number): string|number{
if(typeof x=== 'string' && typeof y === 'string'){
return x + y //字符串拼接
}else if(typeof x === 'number' && typeof y === 'number'){
return x + y //数字相加
}
}
//函数调用
//两个参数都是字符串
console.log(add('zhuge', 'kongming'));
//两个参数都是数字
console.log(add(10, 20));
//此时如果传入的是非法的数据,ts应该会提示错误
// console.log(add('zhuge', 10));
// console.log(add(10, 'zhuge'));
})()
泛型
在定义函数,接口,类的时候不能确定要使用的数据的类型,而是在使用的时候才能确定数据的类型
(()=>{
//需求:定义一个函数,传入两个参数,第一个参数为数据,第2个参数为数量,返回一个数组,包含数量个数据
function getArr(a:string, b:number):string[]{
const arr:string[] = [];
for(let i=0; i<b; i++){
arr.push(a)
}
return arr
}
function getArr1(a:number, b:number):number[]{
const arr:number[] = [];
for(let i=0; i<b; i++){
arr.push(a)
}
return arr
}
function getArr2(a:any, b:number):any[]{
const arr:any[] = [];
for(let i=0; i<b; i++){
arr.push(a)
}
return arr
}
function getArr3<T>(a:T, b:number):T[]{
const arr:T[] = [];
for(let i=0; i<b; i++){
arr.push(a)
}
return arr
}
console.log(getArr3('abd', 3))
console.log(getArr3(10.2, 3))
console.log(getArr3(10.2, 3)[0].toFixed(2))
console.log(getArr3('asd', 3)[0].split(''))
})()
多个泛型参数
函数中有多个泛型参数
(()=>{
function getMsg<K, V>(value: K, value2:V):[K, V]{
return [value, value2]
}
const arr1 = getMsg<string, number>('jack', 10.2);
console.log(arr1[0].split(''));
console.log(arr1[1].toFixed(1));
})()
泛型接口
在定义接口时,为接口中的属性或者方法定义泛型类型,在使用接口时,再指定具体的泛型类型
(()=>{
// 需求:定义一个类, 用来存储用户相关的信息、
//定义一个用户信息类
class User{
id?: number
name: string
age: number
constructor(name:string, age:number){
this.name = name
this.age = age
}
}
//定义一个泛型接口
interface IBaseCRUD<T>{
data:Array<T>
add:(t:T) => T
getUserId:(id:number) => T|undefined
}
//定义一个类,可以针对用户的信息对象进行增加及查询的操作
//CRUD----》create, Read, Update, Delete
class UserCRUD implements IBaseCRUD<User>{
//用来存储多个User类型的用户信息对象
data: Array<User> = [];
//方法用来存储用户信息对象的
add(user:User):User{
user.id = Date.now() + Math.random()
//把用户信息对象添加到data数组中
this.data.push(user)
return user;
}
//方法根据id查询指定的用户信息对象
getUserId(id:number|undefined):User|undefined{
return this.data.find(user=>user.id===id)
}
}
const userCRUD:UserCRUD = new UserCRUD();
userCRUD.add(new User('jack', 20))
const {id} = userCRUD.add(new User('lucy', 21))
console.log(userCRUD.getUserId(id));
console.log(userCRUD.data);
})()
泛型类
定义一个类,类中的属性值的类型是不确定的,方法中的参数及返回值的类型也是不确定的
(()=>{
//定义一个泛型类
class GenericNumber<T>{
//默认属性值的类型是泛型类型
defaultValue: T
add:(x:T, y:T) => T
}
//在实例化类的对象的时候,再确定泛型类型
const g1:GenericNumber<number> = new GenericNumber<number>()
g1.defaultValue = 100
g1.add = function(x, y){
return x + y
}
console.log(g1.add(g1.defaultValue, 20));
const g2:GenericNumber<string> = new GenericNumber<string>()
g2.defaultValue = 'haha'
g2.add = function(x, y){
return x + y
}
console.log(g2.add(g2.defaultValue, 'd'));
})()
泛型约束
如果我们直接对一个泛型参数取length属性,会报错,因为这个泛型根本就不知道它有这个属性
(()=>{
//定义一个接口,用来约束将来的某个类型中必须要有length这个属性
interface ILength {
//接口中有一个length属性
length: number
}
function getLength<T extends ILength>(x:T):number{
return x.length
}
console.log(getLength<string>('what is it'));
// console.log(getLength<number>(123)); //报错,number不满足泛型接口,number类型没有length属性
})()
声明文件
当使用第三方库的时候,我们需要引用它的声明文件,才能获得对应的代码补全,接口提示等功能
声明语句:如果需要ts对新的语法进行检查,需要加载对应的类型说明代码
declare var jQuery:(selector: string) => any;
声明文件:把声明语句放到一个单独的文件(jQuery.d.ts) 中,ts会自动解析到项目中所有声明文件(xx.d.ts)
下载文件声明: npm install @types/jquery --save-dev
内置对象
(()=>{
//ECAMScript的内置对象
let b: Boolean = new Boolean(1)
let n: Number = new Number(true);
let s: String = new String('asd');
let d: Date = new Date()
let r: RegExp = /^1/
let e: Error = new Error('error message')
// b = true
console.log(b);
})()
(()=>{
const div: HTMLElement|null = document.getElementById('test')
const divs: NodeList = document.querySelectorAll('div')
document.addEventListener('click', (event: MouseEvent)=>{
console.dir(event.target);
})
const fragment:DocumentFragment = document.createDocumentFragment()
})()