C/C++教程

typescript基础学习

本文主要是介绍typescript基础学习,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一:typescript介绍和环境搭建

1.typescript介绍

TS是属于JavaScript的超集,可以编译成纯JavaScript,TS新增了许多特性,例如数据类型、类、继承以及接口等。

2,typescript环境搭建

i.项目环境搭建

安装node.js=>全局按照typescript:npm i -g typescript

ii.编写一个ts文件并编译

编写一个.ts文件=>命令行tsc b.ts=>编译成一个JavaScript文件。

iii.vscode配置自动编译

在当前项目执行指令

tsc --init

在项目根目录生成一个tsconfig.json文件:

 

自动编译,选择终端>运行任务>监视typescript。之后每次文件一保存,就会自动编译。

二:typescript的数据类型

typescript的数据类型主要有:

布尔类型(boolean)

数字类型(number)

字符串类型(string)

数组类型(array)

元组类型(tuple)

枚举类型(enum)

任意类型(any)

null和undefined

void类型

never类型

格式:let 变量:类型=变量值

1,boolean类型

let flag:boolean=[false|true]

2.数字类型

let num:number=12

let float:number=12.12

3.字符串类型

let str:string='hello world'

4.数组类型

let array:number[]=[1,2] let str:string[]=['1','2']

泛型方式

let arr:Array<string>=['we','eo']

5.元组类型

元组类型是数组类型的一种,可以指定数组中每个元素的类型

let tup:[string,number,boolean]=['we',21,false]

6.枚举类型

定义:

enum 枚举变量名{

      枚举类名=枚举值,

      枚举类名1=枚举值1,

       .........

}

使用

var  变量:枚举类型=枚举变量名.枚举名

var 变量名=枚举变量名.枚举名

 

enum flag{
   success=1,
   errorM=-1
}
var F:flag=flag.success
//var F=flag.success
console.log(F);

 单枚举项都为number类型的时候(默认为number)如果不赋值,打印的将是元素的索引值,如果元素的前面的元素有值,则改元素打印的值为前面元素加1

 7.任意类型(any)

在我们获取dom节点的时候,不知道应该取什么类型,就可以使用any类型

let dom:any=document.getElementById('app')

 

8.null和undefined

i.undefined

在typescript必须指定其变量类型,要不然会报错,这时候我们只定义不赋值的时候,在使用变量的时候变量也会报错

这时候就提供了undefined解决这个问题,指定变量为其他类型或者undefined,这时不赋值就不会报错了。

ii.null

表示为空,和undefined有点类似,一个变量可能是空或者undefined

let num:number|undefined|null

iii.void类型

表示定义无返回值的方法

function fn():void{
    console.log('q'); 
}

相对的可以指定返回值的类型

function fn():number{
    return 1
}

 

9.never类型

never表示其他类型的(包括null和underfined)子类型,表示从未出现过的值,是一个隐含的类型。主要体现在

never类型只能被never类型赋值

let num:number; num=12

如上声明num类型的时候,该变量赋值时只能赋一个number类型的值。如果不是会报错

let nev:never
nev:(()=>{
throw new Error('错误')
})()

真正的写法如上,但是不常用,一般使用any或者多类型定义来解决。

let a:null|undefined|number|string|boolean

三.typescript函数

1,typescript函数的定义

有返回值

function name(params:type):type {
    return paramType
}
或
let name=function (params:type):type {
    return paramType
}

无返回值

function name(params:type):void {
   
}
或
let name=function (params:type):void {
  
}

四.typescript的类、接口

对象:具体的,实际的,代表一个事物,建一个对象的属性和行为封装在一起,就构成一个类。

 

1.typescript定义类

使用的是es6的新特性:关键字class

class Person{
    
}

2.类中的属性

类中有三大属性:变量、构造方法、方法。

其中变量又可以分为:公共变量、私有变量、保护变量

class Person{
    public name:string;//如果是公共方法,public可以省略
    constructor(name:string){
        this.name=name
    }
    eat(){
        console.log('普通方法'); 
    }
}

i.实例化类

var person=new Person('小明')

调用:

person.eat() person.name//使用public修饰,可以直接访问,如果使用private修饰,只能向外提供公共的访问方法get/set方法

ii.private关键字:静态变量

特点:静态变量不可以直接调用,只能通过提供对外访问方法,

好处:保证了类中变量的安全性,不使外部直接访问到。

class Person{
    private name:string;
    constructor(name:string){
        this.name=name
    }
    eat(){
        console.log('普通方法');
        
    }
    getName(){
        return this.name
    }
    setName(name:string){
        this.name=name
    }
}
var p =new person()

直接通过:p.name是无法访问该变量,只能通过getName/setName间接访问和修改

p.getName()

p.setName("小红")

 3.类实行继承:extends

继承只能继承父类的公共属性,私有属性无法继承。

继承只能单继承,不能多继承。

类只能单继承。

4.类的修饰符

typescript提供了三个修饰符:public(保护)、protected(保护)、private(私有)

默认是public,定义时可以省略。

protected:被修饰的属性只能在继承体系内使用。

private:定义的属性只能在该类使用。

5.类的静态属性和静态方法:关键字static

在es5中,直接通过构造函数的方式添加方法和属性,叫做静态方法和静态属性

function Person(){
   name:"xiaozhi"
   setName:()=>{
    console.log("实例方法")
   }
}
Person.age="12"
Person.setAge=function(){
    console.log('静态方法');
}
//静态调用
Person.setAge()
Person.age
//实例调用
var p=new Person()
p.setName()
p.name

typescript中的静态方法和静态实例

静态方法和属性和实例方法和属性的区别

静态方法和实例可以直接通过类名调用,实例方法和属性需要使用实例化类名调用。

静态方法和属性不会随着类的调用后而消失,实例方法和属性则会。

定义静态属性和静态方法

 class Person{
    static name2='xiaozhi';
    static  run(){
        console.log("go"); 
    }
}
Person.name2
Person.run()

6.类的多态

多态:同一个事物表现出不同的状态表示多态。多态中的父类方法值定义不实现功能。

例如:水有固态、液态和气态三种状态,那么父类就是水,而子类就是不同状态的水。

那就实现以下这个代码吧

class water{
     name:string;
    constructor(name:string){
        this.name=name
    }
    waterState(){}
}
class Gutai extends water{
    constructor(name:string){
        super(name)
    }
    waterState(){
        console.log("我是"+this.name+"水"); 
    }
}
class Yetai extends water{
    constructor(name:string){
        super(name)
    }
    waterState(){
        console.log("我是"+this.name+"水"); 
    }
}
class Qitai extends water{
    constructor(name:string){
        super(name)
    }
    waterState(){
        console.log("我是"+this.name+"水"); 
    }
}
var gutai=new Gutai('固态')
gutai.waterState()
var yetai=new Yetai('液态')
yetai.waterState()
var qitai=new Qitai('气态')
qitai.waterState()

7.抽象类:abstract

抽象类就是类的基类

举个例子:人都要吃饭,都要睡觉,都要呼吸,将吃饭睡觉呼吸提取出来放到一个类,并有abstract修饰,就变成一个抽象类了,只是抽象类只提供行为不执行行为,你吃什么饭,睡觉睡得怎样呼吸快慢抽象类不管,抽象类只管你要是继承我,这些行为必须得有。

抽象类有一下特性:

抽象类不能被实例化,

抽象类的成员可以不是抽象成员,但是有抽象成员的类必须是抽象类。

子类继承抽象类必须重写抽象类的抽象方法。

子类可以是一个抽象类,表示该子类必须有这个行为。

总结一下:抽象类就是提供行为而不执行行为的类【人是一个抽象类,都得吃饭,小米是一个具体类(也可以是抽象子类,这个看下面)吃什么不管,你想吃啥就吃啥,但是必须得有吃的这个行为】。

abstract class Person{
    abstract eat():any;
}
class Xiaoming extends Person{
     food:string;
     constructor(food:string){
         super()
         this.food=food
     }
      eat(){
          console.log("小明会吃,正在吃"+this.food);
          
      }
}
var xiaoming=new Xiaoming("窝窝头")
xiaoming.eat()

抽象类的成员:

成员变量:既可以是变量也可以常量,当不能用abstract修饰(修饰就无法初始化变量了)

成员方法:既可以是一般方法也可以是抽象方法,一般方法无需重写(想要访问抽象类的变量最好重修)

构造函数:有,作用是初始化成员变量。

abstract class Person{
     food:string;
    constructor(food:string){
        this.food=food
    }
    abstract eat():any;
    run(){
        console.log("小明边跑边吃着"+this.food);
        
    }
}
class Xiaoming extends Person{
     constructor(food:string){
         super(food)
     }
      eat(){
          console.log("小明会吃,正在吃"+this.food);  
      }
}
var xiaoming=new Xiaoming("大白菜")
xiaoming.eat()
xiaoming.run()

五.接口:interface

typescript中接口就是对json数据进行约束或者对类进行拓展,约束json数据的时候主要是对对象数据进行约束,规范对象数据的内部数据格式必须和接口一致。

1.接口的约束作用

a.接口对json数据进行拓展(即对象)

 

interface PersonIf{
    name:string;
    age:number;
    sex:boolean
}
function getPerson(obj:PersonIf):string{
    return ''
}
var obejct={
    name:'小孩',
    age:18,
    sex:false
}
getPerson(obejct)

 解读:以上代码的getPerson函数接收一个类型为PersonIf的对象,PersonIf则是约束了转入对象参数的格式的接口,即obejct对象必须要有和接口一样的属性,要会报错。

值得注意的是:接口有的属性,对象必须要有;对象有的属性,接口不一定强制有;

 

interface PersonIf{
    name:string;
    age:number;
    sex:boolean
}
function getPerson(obj:PersonIf):string{
    return return obj.name+obj.age
}
var obejct={
    name:'小孩',
    age:18,
}
getPerson(obejct)

一般情况下,后端接口传过来的json数据可能会根据请求参数的不同,传到前端的数据可以字段也会不太一致,或者有些什么直接参数丢失等。这时候,我们可以把interface的约束属性变成一个可选值。使用“?”符号

此时接口有的属性,对象中不一定要有了

interface PersonIf{
    name?:string;
    age?:number;
}
function getPerson(obj:PersonIf):any{
    return obj.name
}
var obj={
    name:'小孩',
}
 console.log(getPerson(obj));

值得注意的是:可选参数的时候,有返回值,方法定义的放回类型应该是一个any,因为不知道这个name是可选的,有可能是一个underfined。

还有一个方式是:(可看对数组的约束,有讲解)

b.接口对函数进行约束

接口对函数进行约束主要是对函数的参数和返回值进行约束

格式:

interface 接口名{
    (参数名1:参数类型1,参数名2:参数类型2,...):返回值类型
}
interface PersonIf{
    (name:string,age:number):string
}
let fn:PersonIf=function(name:string,age:number):string{
    return name+age
}
console.log(fn('xiaozhi',18));

c.接口对数组(或对象)的约束

格式:

interface 接口名{
    [index:number]:数组元素类型
}

如果是数组,index的类型必须是number,如果是对象,可以是string(不常用,原因是对象的属性很多情况类型是不同的,可以使用any解决)

约束数组代码:

interface array{
    [index:number]:string
}
let arr:array=['12','34']
console.log(arr);

约束对象代码:

interface obj{
    [index:string]:any
}
let duixiang:obj={
    name:'xiaozhi',
    sex:18,
    jop:{
        p1:'老师',
        p2:'家长'
    }
}
console.log(duixiang.jop.p1,duixiang.name);

4,接口对类的拓展

和抽象类有点类似,如果把类比作一个人的话,把人共有的提取出来,封装成一个抽象类;往人身上添加东西的就是接口。

比如张三是一个人,是个人就要继承人的抽象类(不吃饭、不睡觉不呼吸就不是人了);张三这个人追求个性,所以他实现了接口(红眉毛、绿口红才有个性)。这时候王五也是个人,也需要继承人这个抽象类,但是他不追求个性,所以他不实现这个接口。

类实现接口可以这么理解!!!

interface PersonIf{
    name:string;
    age:number;
    kouhong():any;
}
class person implements PersonIf{
    name:string;
    age:number;
    constructor(name:string,age:number){
        this.name=name
        this.age=age
    }
    kouhong(){
        console.log(this.name+'今年'+this.age+'岁,所以涂口红');
    }
}
var p=new person("张三",18)
console.log(p.kouhong());

 注意:接口中的方法只能定义不能有方法体;类实现接口必须重写接口内的全部属性和方法;接口可以多实现;接口和接口可以实现继承(一般使用多实现就可以了,没必要继承)

interface PersonIf1{
    id:string
    age:number;
    kouhong():any;
}
interface PersonIf2{
    name:string;
    meimao():any;
}
class person implements PersonIf1,PersonIf2{
    id:string;
    name:string;
    age:number;
    constructor(name:string,age:number,id:string){
        this.name=name
        this.age=age
        this.id=id
    }
    kouhong(){
        console.log(this.name+'今年'+this.age+'岁,所以涂口红');
    }
    meimao(){
        console.log("眉毛"); 
    }
}
var p=new person("张三",18,'11111')
console.log(p.kouhong());

 5.注释

类可以同时继承一个类和实现多个接口

六.泛型

泛型:泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。.泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

1.函数泛型

有这么一个需求,一个函数可以返回任意类型,可以使用any,但是使用any异味这放弃了类型检查,这时候就需要泛型来解决,使其传入什么类型就放回什么类型。

泛型是一个参数化类型,可以检查参数列表的合法性。

function fn<T>(name:T):T{
    return name
}
//这种调用方式相当any
//console.log( fn('小智'));
//console.log( fn(12));
//console.log( fn(false));
console.log( fn<string>('小智'));
console.log( fn<number>(12));
console.log( fn<boolean>(false));

上面的代码中,<T>表示泛型,T表示泛型类型名称(传入什么就是什么),这里的泛型类型也可以是typescript中的某个数据类型。在这个代码中,返回的是泛型类型,我们也可以指定返回的类型(不是泛型)

function fn<Y>(name:Y):string{
    return 'CODD'
}
console.log( fn<string>('小智'));

 但是以下代码是错误的

function fn<Y>(name:Y):string{
    return name
}
console.log( fn<string>('小智'));

 因为返回的是name,而name的类型不一定是string类型,有可能是number类型。

 2.泛型类

有这么一个需求,定义一个类,该类中功能是添加数组元素。要求只能添加同一个数据类型的数据。

class AddArray<T>{
     array:T[]=[];
    add(item:T):void{
        this.array.push(item)
    }
    getArray():any{
        return this.array
    }
}
var m=new AddArray<string>()
m.add('xiozhi')
m.add('xiaolei')
m.getArray()
console.log(m.getArray());
var m2=new AddArray<number>()
m2.add(12)
m2.add(13)
console.log(m2.getArray());

 

3.泛型接口

i.泛型接口之函数泛型接口

顾名思义,就是把泛型加载接口中的函数上

interface DataInterface{
    <T>(str:T):T
}
var getData:DataInterface=function<T>(name:T):T{
    return name
}
console.log(getData("xiaozhi"));

ii.泛型接口之接口泛型接口

顾名思义,就是将泛型定义在接口上。

interface DataInterface<T>{
    (str:T):T
}
function fn<T>(name:T):T{
    return name
}
var getData:DataInterface<string>=fn
var getData1:DataInterface<number>=fn
console.log(getData("小白"));
console.log(getData1(12));

在定义的时候就直接指定泛型类型,简化写法(函数只调用一次)

interface DataInterface<T>{
    (str:T):T
}
var getData:DataInterface<string>=function<T>(name:T):T{
    return name
}
console.log(getData("小白"));

七.typescript模块化

使用了JavaScript模块化方式,使用export向外暴露模块内容,使用import引入暴露出来的数据。

新建如下文件

每个文件中写入:

user.ts

import userImp from './userImp'
class User implements userImp{
    name:string
    age:number
    constructor(name:string,age:number){
        this.name=name
        this.age=age
    }
    getName():string|undefined{
        return this.name
    }
    setName(name:string):void{
        this.name=this.name
    }
    getAge():number|undefined{
        return this.age
    }
    setAge(age:number){
        this.age=age
    }
}
export default  User

userImp.ts

interface userImp{
    name:string
    age:number
}
export default userImp

 index.ts

import User from './user'
var user= new User('XIAOHZI',18)
console.log(user.age);
console.log(user.name);

 由于模块化编程是编译成的js无法被浏览器运行,这时候需要通过node来运行jindex.js文件

 1.命名空间

在写typescript的时候,定义类或者变量多的时候,可能会导致命名冲突,这时候我们可以使用命名空间来把代码分开,格局命名冲突。

namespace 空间名{........}

在命名空间内需要通过export将需要导出的内容导出就可以了。

namespace A{
   export class a{
        name:string|undefined
        getName():any{
            console.log(this.name);
            return this.name
        }
    }
}
namespace B{
 export  class a{
        name:string|undefined
        getName():any{
            console.log(this.name);
            return this.name
        }
    }
}
var Aa=new A.a()
var Ba=new B.a

如果想要将命名空间模块化,也需要使用export将空间导出

export namespace A{
   export class a{
        name:string|undefined
        getName():any{
            console.log(this.name);
            return this.name
        }
    }
}

八.装饰器

装饰器是一个方法,可以向一个类、方法、属性、参数注入内容,用于扩展其功能。

使用方式

@装饰器名(param:any){...}

接收一个参数,这个参数表示的是当前类、方法、属性或者参数。

主要:要支持装饰器,需要在ts配置文件中打开:

"experimentalDecorators": true,

1.无参类装饰器

无参装饰器params就是类本身

function logDom(params:any){
    //param表示的是类本身
    // 向当前类添加属性
   params.protoType.age='12'
    // 向类里面添加方法
    params.protoType.getAge=function(){
        console.log("11");   
    }
}
@logDom
class Dome{
    constructor(){}
    show():void{
        console.log("累呀");
    }
}
var dome:any=new Dome()
console.log(dome.age);
dome.getAge()

2.有参装饰器:返回一个函数

有参装饰器的params表示的是传入的参数,target表示类本身。

function logDom(params:any){
    //param表示的是类本身
    // 向当前类添加属性
    console.log(params)
    return function(target:any){
        target.protoType.age='12'
        // 向类里面添加方法
        target.protoType.getAge=function(){
            console.log("11");
            
        }
    }
  
}
@logDom("你好")
class Dome{
    constructor(){}
    show():void{
        console.log("累呀");
    }
}
var dome:any=new Dome()
console.log(dome.age);
dome.getAge()

类装饰器还可以通过重载的方式修改累的构造函数和方法

function decorateDome(target:any){
    return class extends target{
        url:any='你好'
        getUrl(){
            console.log("不好的");
        }
    }
}
@decorateDome
class Dome{
    url:string|undefined;
    constructor(){
        this.url="我是一个url"
    }
    getUrl(){
        console.log(this.url);
        
    }
}
var dome=new Dome()
console.log(dome.url,);
dome.getUrl()

3.属性装饰器

属性装饰器返回的函数多出了一个,表示该属性

function decorateDome(params:any){
    return function(target:any,atrr:any){
        target[atrr]=params
    }
    
}
class Dome{
    @decorateDome("属性装饰器")
    url:string|undefined;
    constructor(){
        // this.url="我是一个url"
    }
    getUrl(){
        console.log(this.url);
        
    }
}
var dome=new Dome()
console.log(dome.url);

4.方法装饰器

方法装饰器的作用可以用来监视,修改或者替换方法的定义

方法装饰器运行的时候需要传入三个参数

对于静态成员来说是类的构造函数,对于实例成员是类的原型

成员的名字

成员的属性描述符号

function decorateDome(params:any){
    return function(target:any,methodName:any,desc:any){
      console.log(params);
      console.log(target);
      console.log(methodName);
      console.log(desc);      
    }   
}
class Dome{
    url:string|undefined;
    constructor(){
        // this.url="我是一个url"
    }
    @decorateDome("属性装饰器")
    getUrl(){
        console.log(this.url);
        
    }
}
var dome=new Dome()

 可以看到target就是类的本身,methodName是方法名,desc是对这个方法的表述,而value就是该方法本身

a,使用方法装饰器替换方法

function decorateDome(params:any){
    return function(target:any,methodName:any,desc:any){
          desc.value=function(...args:any[]){
            console.log(args);    
          }
    }   
}
class Dome{
    url:string|undefined;
    constructor(){
    }
    @decorateDome("属性装饰器")
    getUrl(){
        console.log(this.url);   
    }
}
var dome=new Dome()
dome.getUrl("你好","haha")

 

这时候我们可以看到该方法以及被替换了,但是在很多情况下,我们是需要修改这个方法,而不是替换,这时候可以使用apply实现函数的修改

function decorateDome(params:any){
    return function(target:any,methodName:any,desc:any){
          var cMethod=desc.value
          desc.value=function(...args:any[]){
            console.log(args);  
            cMethod.apply(target,args)
          }
    }   
}
class Dome{
    url:string|undefined;
    constructor(){
        // this.url="我是一个url"
    }
    @decorateDome("属性装饰器")
    getUrl(){
        console.log("我感觉还行");  
    }
}
//var dome=new Dome()
var dome:any=new Dome()
dome.getUrl("你好","haha")

 5.方法参数装饰器

作用:当函数被调用的时候,使用方法装饰器向类原型添加一些属性或者方法等。

格式:

 return function(target:any,methodName:any,paramsIndex:any){
      ...  
    }

 paramsIndex:表示该参数的索引

function decorateDome(params:any){
    return function(target:any,methodName:any,paramsIndex:any){
      console.log(paramsIndex); 
      target.apiUrl="哈哈哈"    
    }   
}
class Dome{
    url:string|undefined;
    constructor(){
        // this.url="我是一个url"
    } 
    getUrl(@decorateDome("hello") name:any){
    }
}
var dome:any=new Dome()
dome.getUrl("你好")
console.log(dome.apiUrl);

 6.装饰器的执行顺序

属性装饰器>方法装饰器>方法参数装饰器>类装饰器

如果同种装饰器有多个,执行顺序从下到上,右到左。

 

这篇关于typescript基础学习的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!