Typescript简介

Typescript是强类型语言。 ts文件必须转为js文件才能在html中引入。

TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。TypeScript添加了可选的静态类型系统、很多尚未正式发布的ECMAScript新特性(如装饰器 )。2012年10月,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript。当前最新版本为TypeScript3.8。

开始使用Typescript:

1、安装Typescript,终端输入
npm i typescript -g

2、生成ts配置文件tsconfig.json
tsc --init

3、编辑 tscongif.json文件,module指模块代码生成的类型,outDir是输出的路径。

这里我们将module改成amd,outDir改成根目录下的js文件夹。

如果将module改成es2015,则导出的js文件中导入模块的时候需要手动加文件扩展名,例如 import Ball from “./b”; ,需要在b后面加上 .js

typescript保证不是null typescript private_数组

4、VScode->终端->运行任务->ts监视任务,则会自动开启监视,每次保存ts文件时,会自动将ts文件转换为js,并导出在上一步配置好的输出路径下。

typescript保证不是null typescript private_typescript保证不是null_02


5、因为我们导出类型使用的是amd,html文件中require将入口主文件加载进来

<script src="./require.js" data-main="./js/main"></script>

6、main.ts文件内容如下:

//main.ts
//ts中,导入模块时不能加扩展名
import Box from "./b";
let box=new Box();
box.play();

7、b.ts 文件内容如下:

//b.ts
export default class Box{
    constructor(){}
    play(){
        console.log("aaa")
    }
}

8、运行html,就能看到控制台输出了“aaa”。

Typescript基础类型

  • TypeScript 支持与JavaScript 几乎相同的数据类型,此外还增加了元组、枚举类型等。
  • Typescript是强类型语言,变量限定类型后,不能改变变量值的类型。
  • Typescript中所有的变量在定义的时候都需要限定类型,并给定初始值。
  • 函数也需要限定返回值的类型,如果不返回任何值,则定义为void的类型。
  • 函数中的参数也需要限定类型。
  • 构造函数后面不需要限定类型。

数字 number

TypeScript里的所有数字都是浮点数。 这些浮点数的类型是number。 除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量。

let a:Number=3;
let b:number=4;

字符串 string

和JavaScript一样,可以使用双引号(")或单引号(’)表示字符串,也可以使用字符串模版。

let name:string="xiaoming";
let src:string='./js/a.ts';
let sentence:string=`my name is ${name}`;

布尔值 boolean

只有true 跟 false 两个值。

let isDone:boolean=true;

我们在声明函数时,也需要对函数进行类型声明。

// 当前函数必须有返回值,且返回值必须是number类型
function abc():number
{
    var a:number=1;
    return a; 
    // return "a";//会报错
}

// void 表示不返回任何值的函数
function fn(_a:number,b:string):void
{
	// 函数中的参数给定类型,执行函数时,也要限定类型  
    console.log("aaa");
}
fn(3,"a");
fn(3,5);//会报错

//声明class时
class Box{
    // 构造函数后面不需要给定类型
    constructor()
    {}
    play():void
    {}
    run():number
    {return 1;}
}

数组 Array

两种方式可以定义数组。 第一种,可以在元素类型后面接上[ ],表示由此类型元素组成的一个数组;第二种方式是使用数组泛型,Array<元素类型>。
数组中元素的类型必须相同。

let list:number[]=[1,2,3];
//数组泛型
let list:Array<number>=[1,2,3];

元组 Tuple

数组中元素的类型必须相同,那如果元素类型不相同,在这里,我们把它叫做元组。

var list:[string,number,boolean];
list=["a",10,true];
list=["b","c",10];//报错
console.log(list[0].toFixed(2));//报错,因为list[0]是字符串
console.log(list[1].toFixed(2));//10.00

枚举 enum

默认情况下,从0开始为元素赋值;也可以手动给元素赋值。类似于ES6中,class的静态属性。

enum STATUS{WALK,RUN};
var s:STATUS=STATUS.WALK;
console.log(s);//0

enum STATUS{WALK="walk",RUN="run"};
var s:STATUS=STATUS.WALK;
console.log(s);//wakl

任意值 any

不确定的通用类型,使用场景比如来自用户输入或第三方代码库,这种不确定类型的动态内容。不过尽量不要使用。

var a:any=1;
a="abcde";//不会报错
var b=2;//声明变量时,没有限定类型,默认为any类型

let list: any[] = [1, true, "free"];
list[1] = 100;
console.log(list);//[1,100,"free")

空值 void

主要用于函数,不返回任何值。当声明一个void类型的变量时,只能为它赋予undefined。

function fn(_a:number,b:number):void
{
	let aa:void=undefined;
    console.log(_a+b);
}

null和undefined

只适用于null和undefined

let u: undefined = undefined;
let n: null = null;

never

  • never表示的是那些永不存在的值的类型。
  • never类型是任何类型的子类型,也可以赋值给任何类型。
  • 任何类型都不可以赋值给never类型(除了never本身之外)。 即使any也不可以赋值给never。
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}

类型断言

  • 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。
  • 类型断言有两种形式。一个是尖括号语法,一个是 as 语法。
  • 当你在TypeScript里使用JSX时,只有as语法断言是被允许的。
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
//等价于
let strLength: number = (someValue as string).length;

typescript 的基础类型就这么多了,接下来我们来看一个案例:

使用ts声明一个对象型数组

//javascript中我们可以这样来声明
var list=[
    {id:1001,name:"苹果",price:10,selected:true},
    {id:1002,name:"香蕉",price:14,selected:false},
    {id:1003,name:"菠萝",price:23,selected:true},
]

//给list限定对象型数组,这样显然是不严谨的,没办法限定对象中每个属性值的类型
var list:Array<object>=[
    {id:1001,name:"苹果",price:10,selected:true},
    {id:1002,name:"香蕉",price:14,selected:false},
    {id:1003,name:"菠萝",price:23,selected:true},
]

//正确写法
class ItemVo{
    id:number=0;
    name:string="";
    price:number=1;
    selected:boolean=false;
    constructor(_id:number,_name:string,_price:number,_selected:boolean){
        this.id=_id;
        this.name=_name;
        this.price=_price;
        this.selected=_selected;
    }
}
var list:Array<ItemVo>=[
    new ItemVo(1001,"苹果",10,true),
    new ItemVo(1002,"香蕉",14,false),
    new ItemVo(1003,"菠萝",23,true),
]

  • public 公有的,实例化对象只能调用public 定义的内容;
  • private 私有的,私有属性在其他类中无法调用,私有方法是不被继承的,也不能override;
  • protected 受保护的,protected定义的变量只能在类和其子类中调用(可以被继承),通过this调用,但是在实例化的对象中是不可以调用;
  • readonly 只读,相当于ES6中的const 定义。
  • 如果在子类中定义的方法和父类的方法相同,在TS中一概认为是override。
  • 如果父类中定义了一个私有方法,则在子类中不能再出现该私有方法。
class Rect{
    public a:number=1; // public 公有的
    protected b:string="a"; // protected 受保护的
    private c:boolean=false; // private  私有的
    // 定义了一个公有的静态属性,静态只读属性 相当于常量
    public static readonly EVENT_ID:string="event_id";
    constructor(){ 
    }
    public play():void
    {
        console.log(this.a);
        console.log(this.b);
        console.log(this.c);
    }
    private move():number
    {
        return this.a;
    }
    protected moveTo():void{
        console.log("ccc");
    }
}

//实例化对象只能调用public定义的内容
var rect=new Rect();
rect.play(); //打印出1 a false
rect.move();//报错,因为move是private定义的
rect.moveTo();//报错,因为moveTo是protected定义的

//class Circle继承于Rect
class Circle extends Rect{
    constructor(){
        super();
    }
    run():void
    {
        console.log(this.a);
        console.log(this.b);
        // console.log(this.c);//this.c报错  因为在Rect中定义c属性是私有的,这个c属性只能用于Rect类中
        // 私有属性在其他类中无法调用
    }
  //父类中有该类的私有方法,私有方法是不被继承的,也不能override
    /*private move():number //报错
    {
        return this.a;
    } */
    protected moveTo():void
    {
    	super.moveTo();//调用父类的moveTo方法
        // 因为父类中这个方法,因此在这里我们可以继承,也可以override
        // 如果在子类中定义的方法和父类的方法相同,在TS一概认为是override 覆盖
    }
}

接口

  • 类只能继承一个,但是接口可以使用多个,接口使用的目的,为了让多个类没有继承关系,但是可以拥有同样的一个方法,这个方法最好使用断言来确定接口。
  • 在接口中,只需要定义方法名称,具体的执行内容在实现的class中来写。
  • interface 定义接口,接口中不能有构造函数。
  • readonly 表示只读。不可重新赋值。
  • 在属性名后面加 ?、问号 表示该属性非必传。
  • [ ]、索引签名,可以存在任意多个的未定义的属性。
  • 接口中不能使用public private protected static关键词。
  • 接口中只定义方法名、参数(参数类型)、返回值类型,不定义方法的详细内容,任何属性都不能定义值,只能定义名字。
  • 用implements 来实现接口,当继承多个接口时,用逗号隔开,实现接口中定义的所有属性和方法都必须使用public。
//定义一个IMovie的接口
interface IMovie{
     update():void;
     name:string;
     age?:number;// ?号表示非必传
     [propName:string]:any;//索引签名,也可以写成[abc:string]:any
     readonly EVENT_ID:string;
}

//定义了一个IRun的接口
interface IRun{
    run():void;
}

//Avatar继承于IMovie
class Avatar implements IMovie{
    // 接口当中定义的所有属性和方法都必须使用public
    public name:string="abc"; 
    public readonly EVENT_ID:string="event_id";
    constructor(){
    }
    public update():void
    {
        //继承于IMovie
    }
}

//Sprite继承于IMovie和IRun,继承多个接口时,用逗号隔开
class Sprite implements IRun,IMovie{
    // 同一个类可以拥有多个接口
    public name:string="abc"; 
    public readonly EVENT_ID:string="event_id";
    constructor(){
    }
    public update():void
    {
		//继承于IMovie
    }
    public run():void
    {
		//继承于IRun
    }
}

var arr:Array<IMovie>=[
    new Avatar(),
    new Sprite()
];

for(var i=0;i<arr.length;i++){
    // 使用断言,如果该对象不是IMovie接口,就不可以执行update方法
    (arr[i] as IMovie).update();
}