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