TypeScript入门

TypeScript起步入门

typeScript的存在,无疑就是为了更加规范化,是继ES5,ES6之后更加高级的一种javascript的超集。他无法被浏览器所识别,所以就要经过编译过后,编译成普通的es5语法。先从头来了解这玩意怎么用:
1、当然是先下载 npm install -g typescript(不知道npm是什么的先去看node.js),下载完可以在命令行打tsc -v来看版本
2、怎么编译呢? 我们可以用命令tsc index.ts这就能编译出es5语法的js,如果我们每写一点就要编译岂不是很麻烦,看下面:
首先在我们项目文件夹输入 tsc --init ,之后会有一个config文件
将outDir解开,后边是输出js的相对路径

image.png

TypeScript数据类型

首先我们要知道常用的数据类型有哪些,平时我们在js中常用的也就bolean,string,number,undefined,null,arr,在ts中增加了几种数据类型,比如:元组,枚举,以及数组的不同使用的方法,下面一个一个来看:

首先有一点,ts中语法要求很严格,比如你定义一个字符串类型的变量,就不能再为他赋值为数值类型。再者比如你使用了一个未定义的变量或者方法也会直接报错,确实很规范化啊
1、字符串类型
为变量定义基本数据类型的时候一把都是下面这种格式 变量:=""

let a:string='my name is xiaoming'
console.log(a)

2、number类型

let b:number=13
// b='as' 报错 不能声明为其他类型的
console.log(b)

3、boolean类型

 let c:boolean=false
 c=true
 console.log(c)

4、数组类型
数组类型和普通的数据类型有些区别,有两种定义方法,这里如果定义了number类型,那么就无法再给数组赋值为字符串类型的元素

let d:Array=[1,2,3]
d=[3,4,5]
console.log(d)

let f:number[]=[1,2,3]
console.log(f)

5、元组类型
元组类型有些类似数组类型,是数组类型的扩展,需要注意的是这里定义了几个变量下边就只能写几个元素,如果不符合定义的规范,就会报错

let e:[number,string]
e=[1,'2']
console.log(e[0])

6、枚举类型
枚举类型 enum 这里主要是为了定义一些固定值 比如状态值,产品固定特性等,下面是具体使用的方法。如果枚举对象没有被赋值,那么打印出来的就是该元素的下标。

enum Color {Red='red', Green='green', Blue='blue'};
let g: Color = Color.Blue;
console.log(g);    // 输出 blue

7、any类型
any是个什么类型呢,他可以说是一个万能类型,就像原来什么都不写的时候,你无论给他什么类型,都不会出错,下面看看具体使用场景

let msg:any='123'
msg=true
console.log(msg)

可以看到他并没有报错,我们来看看下面的场景


image.png

如果你要获取dom元素,那么使用any再合适不过了
8、undefined null类型

let k:undefined
console.log(k)
let l:undefined
l=2 // 这样就会报错
//null同理
let k:null
k=2  //报错

//来看看混合使用的方法
let k:undefined | null | number
k=undefined  //不报错
k=null//不报错
k=2//不报错

9、void类型
这是一个奇怪的类型,叫做不返回值类型,什么意思呢,可以简单的理解为这个函数没有做任何实际性的操作,比如下面这样:

function a():void{
  console.log("a")
}
//像这样没有任何实际影响的方法就用void
function b():number{
  return 123
}

//我们知道ts是非常严格的,会去检查你的代码是否有错,当你上边方法定义的返回数值类型,里边返回的是字符串类型,这样是会报错的。
10、never类型
这种类型主要针对那些无限循环或者抛出异常,无法执行到终点的事件


let x: never;
let y: number;

// 运行错误,数字类型不能转为 never 类型
x = 123;

// 运行正确,never 类型可以赋值给 never类型
x = (()=>{ throw new Error('exception')})();

// 运行正确,never 类型可以赋值给 数字类型
y = (()=>{ throw new Error('exception')})();

// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
    throw new Error(message);
}

// 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
    while (true) {}
}

TypeScript函数

在ts中不止定义变量需要写类型,在写函数中参数的时候也需要写类型,可能是为了更加规范化,而且让人看得第一眼就能明白。

可选参数

我们知道老方法写函数的时候,传参没什么具体要求,传两个形参,只有一个实参,或者传两个实参,只有一个形参,这都是可以的。但是在ts中,你必须传相对应的参数,比如定义了一个形参,你就只能传一个实参,或者定义了两个形参,你就不能只传一个实参,看下面例子

function update(name:string,age?:number){
    console.log(name)
    console.log(age)
}
update('syx')   //age后边?代表可选参数,有问号代表可以传可以不传,如果没有就会报错了
function update(name:string,age:number){
    console.log(name)
    console.log(age)
}
update('syx')   //这是错误示例

默认参数

默认参数和可选参数差不多,也是基于规范,传参必须对应。这里有默认age=18,不传参数也是可以的。如果没有默认参数,就会报错。

function init(name:string,age:number=18){
    console.log(name)
    console.log(age)
}
init('syx')

剩余参数

剩余参数就像es6的参数,用...arg代表,只不过要写上类型

function last(name:string,...arr:number[]){
    console.log(name)
    let count:number=0
    for(var i=0;i

箭头函数

箭头函数完全就是es6的箭头函数,只是变量要加上类型

var a=(x:number)=>{
    return x+10
    console.log(x)
}
a(10) //20

函数的重载

在js中我们知道函数是不能重载的,会覆盖原来定义的函数,在ts中可以重载,但是这里具体意义还没搞懂,后边再补上

function freight(str:string):void
function freight(str:number):void
function freight(x:any,y?:any):void{
    console.log(x)
    console.log(y)
}
freight('syx')
//这里定义了三个同名函数,在使用的时候回自动去匹配应该执行哪个函数。
//转成js代码是这样的
function freight(x, y) {
    console.log(x);
    console.log(y);
}
freight(123);

Typescript接口

接口用关键字interface来定义,这个接口可不是你平时项目前后端对接所谓的接口,可以理解为定义了一种规范,用来约束你所定义的参数,类,对象等等。

传参约束

对于传参约束,我觉得可以联想到平常项目开发中用到的一些传参规范,比如对接的时候,请求方法的传参,像下面这样在传参的时候将规范定义在后边:params:config

interface config{
    interface:string;
    data?:string;  //问号代表可不传
    showLoading:boolean
}

function httpGet(params:config){
    if(params.showLoading){
        api.showLoading()
    }
    axios.get(params.interface,params.data,function(){
        
    })
}
//这样我们在传入参数时就必须按照这种规范来传
httpGet({
    interface:'http://fy/getNewsList',
    showLoading:false,
}) 

上边定义的config接口,规定了必须要传interface,showLoading这两个参数,而且参数类型必须是string和boolean。data后边有个问号,可以是选填。
这样你就可以在你所有的请求中只要有这种约束,就必须按照这种传参来传,保证了传入参数的正确性。

函数类型接口

这种接口可以用在你定义的方法中,用来约束这个方法的传入值以及返回值

//函数类型接口  对方法传入的参数进行约束,以及返回值进行约束 ??? 
interface enctypt{
    (key:string,value:string):string  //代表必须传入key value 返回值必须是string
}

var md5:enctypt=function(key:string,value:string):string{
    return key+value
}
var ssh:enctypt=function(key:string):string{
    return key
}
console.log(md5('张三','123456'))
console.log(ssh('李四','456789')) 

对数组,对象的约束

[index:number]:string代表索引是数字,那么对应的就是数组,因为数组的下标才是数字,string代表这个索引对应的值。

 //可索引接口  对数组的约束

interface UserArr{
    [index:number]:string
}
var arr:UserArr=['aaa','bbb']
console.log(arr[0])

//对对象的约束
interface obj{
    [index:string]:string  //[index:string] 代表索引是字符串 代表这是一个对象 后边string代表值
}
var obj:obj={name:'asd',age:13} //错误 

类类型接口

类类型接口相当于先定义了一个接口,后边凡是想符合我这个接口的类,就必须按照我这种规范来做。这里用implements关键字来代表符合该接口规范,这种接口有点像类的多态,定义一个方法不去实现,让子类去实现这个方法。

//这里Animal类规定了凡是要用我规范的 就必须有name,eat这两个属性和方法
interface Animal{
    name:string;
    eat(name:string):void;
}

class Dog implements Animal{
    name:string;
    constructor(name:string){
        this.name=name
    }
    eat(){
        console.log(this.name+'吃骨头')
    }
}
class Cat implements Animal{
    name:string;
    constructor(name:string){
        this.name=name
    }
    eat(param:string){
        console.log(this.name+'吃'+param)
    }
}
var dog=new Dog('二哈')
var cat =new Cat('杰瑞')
dog.eat()
cat.eat('老鼠')

上述代码表示:构造了Dog和Cat两个类,要求是符合Animal这个接口的规范。可以看到每个类中都有name这个属性,eat这个方法,如果不定义,那么ts在解析时就会报错。

Typescript继承

ts实现构造函数用的是class,就是es6的语法,比如下面这样:

一共分为五步:
1、class构造一个类
2、定义变量类型
3、constructor将变量注册为类的变量,这里要注意类型要一致
4、方法直接写在类里边,定义方法的返回类型
5、实例化一个类,调用方法的时候直接用实例化的类去调用即可

class Person{
    name:string;
    age:number

    constructor(name:string,age:number){
        this.name=name
        this.age=age
    } 

    getName():void{
        alert(this.name)
        alert(this.age)
    }
    setName(name:string):void{
        this.name=name
    }
}
var person = new Person('张三',18)
alert(person.getName())

ts实现继承用的是关键字 extends

我们实现了一个Person类,这里我们实现一个student类想要继承Person类的方法,也是分为三步走:
1、class 类名 extends 父类名
2、construtor将父类的变量继承下来,这里用extends时候,父类的方法会自动注册到子类的身上,如果子类在一个定义了相同的方法,就会覆盖掉父类身上的这个方法
3、实例化一个对象,调用对象身上的方法

class Student extends Person{
    constructor(name:string,age:number){
        super(name,age)
    }
    getName():void{
        alert(`${this.name}是一个好孩子`)
    }
}
var student=new Student('李四',16)
student.getName() //李四是一个好孩子

类关键字 public private protected

public:定义的变量可以在本身以及继承的子类中使用以及外部使用
private 私有变量 只能在自己类中使用
protected 保护变量 可以在本身及自己的子类中使用

class A{
    protected name:string

    constructor(name:string){
        this.name=name
    }

    getName():string{
        return `${this.name}是共有的变量`
    }
}

class B extends A{
    constructor(name:string){
        super(name)
    }
    sayName():void{
        alert(this.name)  //在使用private的时候,这里会报错,因为private只能在本身使用
    }
}
var a=new A('小明')
var b=new B('小刚')

alert(a.name)  //protected 这里会报错,因为不是在本身也不是在他的子类中使用
alert(b.name)

实现静态方法

首先在es5中实现静态方法我们使用下面这种方式,直接在构造的方法上加一个方法

function Base(name){
    //这样在内部定义的就叫实例方法  调用需要先实例化一个对象 然后调用对象的方法
    this.name=name
    this.sayAge=function(){
        alert(this.name)
    }
}
//这样的就叫静态方法 调用可以直接 Base.getAge()
Base.getAge=function(){
    alert(this.age)
}

在ts中使用static去定义静态变量以及静态方法,在调用的时候就可以不用通过实例化一个对象,而是直接去调用。

class Per{
    public name:string;
    public age:number;
    static sex:string;
    constructor(name:string,age:number){
        this.name=name;
        this.age=age
    }
    getName():void{
        alert(this.name)
    }
    static getSex():void{
        alert(this.sex)
    }
}
//静态方法通过static定义 并且可以直接通过类去调用
Per.getSex()

多态

父类定义的方法不去实现 让他的子类去实现 每一个子类有不同的表现

//多态  父类定义的方法不去实现 让他的子类去实现 每一个子类有不同的表现

class Animal{
    name:string;
    constructor(name:string){
        this.name=name
    }

    eat(){

    }
}

class Dog extends Animal{
    constructor(name:string){
        super(name)
    }

    eat(){
        alert(`${this.name}爱吃肉`)
    }
}
class Cat extends Animal{
    constructor(name:string){
        super(name)
    }

    eat(){
        alert(`${this.name}爱吃鱼`)
    }
}
var dog=new Dog('二哈')
var cat=new Cat('杰斯')
dog.eat()
cat.eat()

我们先定义了一个Animal类,在类中定义了一个eat方法,但是我们没有去实现这个方法,而是让子类去实现,每一个子类有不同的变现,从而实现了类的多态性

抽象

抽象类是给其他类提供基准的基类,不能被直接实例化
抽象 anstract 用abstract关键字定义抽象类和抽象方法,抽象类中抽象方法不包含具体的实>现,必须在他的派生类中实现 换句话说 就是在子类中实现
抽象方法只能放在抽象类中 凡是继承了抽象类的子类 必须包含父类中抽象的方法
抽象就是为子类定义一个基准

abstract class Animal{
    name:string;
    constructor(name:string){
        this.name=name
    }
    abstract eat():any
}
// var animal=new Animal()  //不能创建抽象类的实例
class Dog extends Animal{
    constructor(name:string){
        super(name)
    }

    eat(){
        alert(`${this.name}爱吃肉`)
    }
}
class Cat extends Animal{
    constructor(name:string){
        super(name)
    }
    eat(){
        alert(`${this.name}爱吃鱼`)
    }
} 

这里如果子类中没有实现父类的抽象方法,就会报错,并提示抽象类不能被继承,所以我们在写字类继承抽象类的时候,必须实现父类中的抽象方法。

image

Typescript泛型

泛型:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持
实现方法:function method(value:T):T{} 这个T只是用法最多的,但不是写死的,你想用A,B什么来代替也一样
为什么说解决复用性?大家想一下,如果我们要定义一个方法,但是要约束传入的参数和返回的类型一致,也就是说,我传入参数1,要返回number类型,传入‘张三’,要返回string类型,那么现在是不是要写两个方法分别来约束,这就造成了冗余,为了解决这个问题,我们可以使用泛型来定义约束

1、泛型函数

//旧方法约束传参和返回类型保持一致
function getData(value:number):Number{
    return value
}
function getData_c(value:string):string{
    return value
}
//泛型解决传参和返回类型一致
function getData(value:T):T{
    return value
}
console.log(getData(123)) //传入number 那么value必须为number 传入string时就会报错
console.log(getData('123'))

2、泛型类
实现一个类 来找到数组中的最小值,数组元素类型不定

class minCount{
    public list:number[]=[];

    add(value:number):void{
        this.list.push(value)
    }

    min():number{
        let minNum=this.list[0]
        for(var i=0;i

这种方法弊端:辛辛苦苦写了一个类,最终只能比较数字类型,要是想比较字符串类型还要重构,这也太麻烦了,下面看改造成泛型的类

class minCount{
    public list:T[]=[]

    add(value:T){
        this.list.push(value)
    }

    min():T{   
        let minNum=this.list[0]
        for(var i=0;i()
a.add(1)
a.add(2)
a.add(3)
// a.add('1') //错误 不能赋值给number类型
var b=new minCount()
b.add('a')
b.add('b')
b.add('c')

看下泛型都是用在哪些地方
1、类的后边
2、数组类型
3、add方法的传参
4、min方法的返回值

这四处既能够很好的保证你传入的参数和返回参数一致,又能降低冗余,起到了限制的作用和不确定数据类型的约束
3、泛型接口

interface config{
    (value:T):T;
}

var getYear:config=function(value:T):T{
    return value
}

var c=getYear('张三')  //正确
// var c=getYear('张三')  //错误

4、类作为参数约束传入数据的类型
这个举例子来说可能更明了一些:假设你想定义一个方法,这个方法是往数据库传入数据的,但是你要做一个约束,不能让非法类型的数据传入数据库。
比如定义一个用户类,要求传入用户名和密码,这时候如果传入的是性别或者其他属性,肯定是不行的。这时候就要用到类作为参数来约束传入数据的类型,可以理解为接口的特性,就是起到约束的作用。
然后你还有一个文章类,你要传入title,desc,type等属性,这时候如果传入的参数是年龄,大小这些属性肯定也是不行的,这时候你就要用到文章类来约束传入的参数。下面看具体的例子

class User{
    username:string | undefined;
    password:string | undefined;
}

class Artical{
    title:string | undefined;
    desc:string | undefined;
    type:string | undefined
}

class mysqlDB{
    //定义一个方法 传入的参数要遵从User类 
    add(value:User):boolean{
        console.log(value)
        return true
    }
}

//这样定义的类如果想要约束多个类的规范 就必须重复的写多个 这时候使用泛型 就可以避免重复了
class mysqlDB_c{
    //定义一个方法 传入的参数要遵从User类 
    add(value:Artical):boolean{
        console.log(value)
        return true
    }
}

var u=new User()
// u.username='张三'
u.password='123445'
var params={
    name:'09'
}

var DB=new mysqlDB()
var DB_C=new mysqlDB_C()
// DB.add(params)  //错误 这里的params不符合user类的规范
// DB.add(u)

var artical=new Artical()
artical.title='学习'
artical.desc='学习让人进步'

DB_C.add(artical)

这样虽然可以实现约束效果,但是如果还有更多的类约束,那么岂不是要写很多的mysqlDB类,这样显然不可行,下面看使用泛型来解决这个问题。

class User{
    username:string  | undefined;
    password:string | undefined;
}

class Artical{
    title:string | undefined;
    desc:string | undefined;
    type:string | undefined;
    constructor(params:{  //这里再次进行传入参数约束 必须穿入title,desc type 可不传
        title:string | undefined;
        desc:string | undefined;
        type?:string | undefined;  
    }){
        this.title=params.title,
        this.desc=params.desc,
        this.type=params.type
    }
}

//这样定义一个泛型类 在实例化的时候可以传入约束类 避免了重复构造mysqlDB类
class mysqlDB{
    add(value:T):boolean{
        console.log(value)
        return true
    }

    update(value:T,id:number):boolean{
        console.log(value,id)
        return true
    }
}
var u=new User()
u.username='张三'

var a=new Artical({
    title:'好好学习',
    desc:'天天向上'
})

var db=new mysqlDB()
db.add(u)

var db1=new mysqlDB()
db1.add(a)
db1.update(a,12)

这里定义user类和artical类的时候有些区别,artical类直接在内部实现了constructor,这样就可以直接在实例化的时候传参,而且可以看到artical类在构造的时候也是进行了约束的,这样约束之后在传参的时候就必须传入title,desc参数,type参数是可选的

mysqlDB类在定义的时候使用了泛型的方法,确保了实例化对象的时候使用了哪种类约束。使用了哪种规范就必须按照这种方式来传参了。

你可能感兴趣的:(TypeScript入门)