TypeScript简介
TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。
TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。
TypeScript 是一种给 JavaScript 添加特性的语言扩展。增加的功能包括:
- 类型批注和编译时类型检查
- 类型推断
- 类型擦除
- 接口
- 枚举
- Mixin
- 泛型编程
- 名字空间
- 元组
- Await
安装
安装TypeScript
需要使用到npm,所以先确保本地有安装nodejs。
使用npm安装
npm install -g typescript
安装完成后在命令行窗口使用tsc -v
查看版本号
如图所示表示安装成功
Hello World
首先编写第一个hello world程序
创建一个myFirstTs.ts
文件,写入以下代码
let hello: String = 'hello TypeScript'
console.log(hello)
ts代码需要编译成js代码方可运行
使用tsc命令
typescript编译器会将.ts文件编译为.js文件,然后用node命令运行js
基础类型
TypeScript提供了与JavaScript几乎相同的数据类型,同时还添加了枚举类型
数值型
TypeScript里的所有数字都是浮点数。 这些浮点数的类型是 number
。 除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量。
let num1: number = 10 // 十进制 10
let num2: number = 0b10 // 二进制 2
let num3: number = 0o10 // 八进制 8
let num4: number = 0x10 // 十六进制 16
字符串
字符串类型为string
,和JavaScript一样,可以使用双引号( "
)或单引号('
)表示字符串,还可以使用模板字符串
// 字符串类型
let str1: string = "String"
let str2: string = `Hello ${str1}`
布尔值
布尔值就是简单的true/false,和其他语言一样,叫做boolean
let bool: boolean = false
数组
数组有两种定义方式
第一种:在元素类型后加上[]
,表示由此类型元素构成的数组
第二种:使用数组泛型,Array<元素类型>
let list1: number[] = [1,2,3,4]
let list2: Array<number> = [1,2,3,4]
可以通过下标访问数组元素,例:list1[0]
元组
元组类型和数组类似,元组类型表示一个已知元素类型和元素数量的数组,元组中的元素类型可以不同
let tuple: [string,number] = ['string', 123]
元组和数组一样,可以通过下标访问元组元素
枚举
enum
类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
// enum 关键字定义一个表示性别的枚举
enum Gender{
male = 0,
Female = 1
}
// 在对象中使用枚举
let obj = {
name: '太阳当空丶赵',
sex: Gender.male
}
console.log(obj.sex === Gender.male); // true
枚举类型提供的一个便利是你可以由枚举的值得到它的名字,当我们知道数值为0,但是不知道映射到哪个名字时,可以查找相应的名字
enum Gender{
male = 0,
Female = 1
}
let str:string = Gender[0]
console.log(str)
任意类型
声明变量时如果不指定类型,ts解析器自动判断变量类型为any(隐式any)
声明为 任意类型any
的变量可以赋予任意类型的值。
let x: any = 1; // 数字类型
x = 'I am who I am'; // 字符串类型
x = false; // 布尔类型
我们可以对 any 进行任何操作,不需要检查类型。
但是没有类型检查就没有意义了,跟写JS一样。很不安全。
必要时可以使用 unknown
,unknown
类型不能赋值给除了 unknown
或 any
的其他任何类型,使用前必需显式进行指定类型,或是在有条件判断情况下能够隐式地进行类型推断的情况
let num:number;
let b:unknown
b = '123'
// num = b; // 报错
// 类型断言,告诉解析器变量实际类型
num = b as number;
num = <number> b;
// 类型保护
if(typeof b === 'string'){
console.log(b.length)
}
void
某种程度上来说,void
类型像是与any
类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void
function fn():void {
console.log("abcdefg")
}
声明一个void
类型的变量没有什么用,因为你只能为它赋予undefined
和null
Null 和 Undefined
TypeScript里,undefined
和null
两者各自有自己的类型分别叫做undefined
和null
。 和 void
相似,它们的本身的类型用处不是很大:
let u: undefined = undefined;
let n: null = null;
默认情况下null
和undefined
是所有类型的子类型。 就是说你可以把 null
和undefined
赋值给number
类型的变量。
Never
never
类型表示的是那些永不存在的值的类型。
never
类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
类型断言
有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息
类型断言可以告诉解析器变量实际类型
let str:unknown = '123'
// as 语法
let num1:number = str as number
// 尖括号语法
let num2:number = <number>str
变量声明
和js一样,我们可以通过var,let,const关键字声明一个变量。let在很多方面与var相似,但是没有变量提升,而且使用的是块作用域,避免了一些常见问题。const则是对let的一个增强,能够组织对同一个变量的二次赋值。
TypeScript 变量的命名规则:
-
变量名称可以包含数字和字母。
-
除了下划线 _ 和美元 $ 符号外,不能包含其他特殊字符,包括空格。
-
变量名不能以数字开头。
类型别名
类型别名会给一个类型起个新名字。类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型
// 类型别名
type myType1 = string;
let str:myType1 = '123'
console.log(typeof str) // string
type myType2 = 1 | 2 | 3
let num:myType2
num = 1
console.log(typeof num) // number
// 描述一个对象的类型
type myType3 = {
name: string
age: number
}
const obj:myType3 = {
name: "123",
age: 123
}
变量声明
let [变量名] : [类型] = 值;
// 声明变量,同时指定类型
let num:number;
let str:string;
let bool:boolean;
let a:any; //声明为any的变量可以赋予任何类型的值
a = 123
a = '123';
// 声明变量时如果不指定类型,ts解析器自动判断变量类型为any(隐式any)
// any类型的变量可以赋值给任意类型的变量,不会报错,必要时可以使用unknown
//
let b:unknown
b = '123'
num = a;
let num: number = 10
// 字面量,限制该该变量的值就为10,不能改变
let data:10;
// 可以使用 | 来连接多个类型
let x: 10 | 20;
let s: string | number;
s = '123';
s = 123
函数声明
// 函数声明
function sum(numa:number,numb:number):number{
return numa + numb
}
// void表示空,表示没有返回结果的函数
function fn():void{
// return 123
console.log('123')
}
// 函数结构声明
let d: (a:number, b:number) => number
d = function (num1, num2):number{
return num1 + num2
}
ES6中提供的解构赋值也可使用到函数声明中
// 自定义类型
type C = { a: string, b: number }
// 解构用于函数声明
function fn({ a, b }: C): void {
console.log(b + parseInt(a))
}
// 定义一个对象
const obj:C = {
a:'1',
b: 1
}
fn(obj)
对象声明
用object
表示一个js对象
let a:object;
a = {}
// {}用来指定对象中包含的属性
let b:{name:string,age:number,sex?:string} // ?表示属性可选择
b = {name: "孙悟空", age: 1000}
b = {name: "孙悟空", age: 1000,sex: '男'}
// 添加任意属性
let c:{name:string, [propName:string]:any}
c = {name:'猪八戒',age: 1000,sex: '男'}
编译选项
正常编译ts文件
tsc app.ts
每次修改ts代码后,都需要手动编译
ts文件监视,自动编译
tsc app.ts -w
监听文件变化,每当文件变化,就会自动编译
使用tsc直接编译所有ts文件
在项目文件夹下创建 tsconfig.json
然后就可以使用tsc命令直接编译全部ts文件,还可以使用tsc -w 监视全局文件
tsconfig.json
是ts编译器的配置文件,ts编译器可以根据它的配置信息对代码进行编译
下面是一些常用配置
{
/*
"include" 指定那些ts文件需要被编译,
/** 表示任意目录
/* 表示任意文件
"exclude" 指定排除那些文件或文件夹
默认值:["node_modules","bower_components","jspm_packages"]
*/
"include": [
"./src/**/*"
],
// "exclude": [
// "./src/*"
// ]
/*
compilerOptions 编译器选项
*/
"compilerOptions": {
"target": "ES6", //target用来指定ts被编译成es的版本
"module": "ES6", // 指定模块化的规范
// lib用来指定项目中会使用到的库
// "lib": [
// "dom"
// ]
"outDir": "./dist", //指定编译文件所在的目录
"allowJs": true, //是否编译js文件,默认false
"checkJs": true, //检查js代码是否符合语法规范
"removeComments": true, //是否移除注释
"noEmit": false, //是否生成编译文件
"noEmitOnError": true, // 当有错误时不生成编译后的文件
"strict": true, //所有严格检查总开关
"alwaysStrict": true, // 是否使用严格模式,默认false
"noImplicitAny": true, //不允许使用隐式any
}
}
配置tsconfig.json
后,便可以直接使用tsc命令编译配置文件夹下的所有ts文件,还可以使用tsc -w
监听全局文件。
面向对象
TypeScript 是面向对象的 JavaScript。类描述了所创建的对象共同的属性和方法。TypeScript 支持面向对象的所有特性,比如 类、接口等。
类的简介
使用class关键字定义一个类,使用 new 关键字来实例化类的对象
对象包含 属性
和 方法
两个部分
属性主要分为实例属性和静态属性
直接定义的属性叫做实例属性,实例属性只能通过 new 实例的方式使用
例:const person = new Person();
使用static关键字描述的属性为静态属性,静态属性可直接通过类名访问
class Person {
// 实例属性
name:string = "赵久燚"
age:number = 21
// readonly关键字表示只读属性 该属性不能修改
readonly sex:string = "男"
// 静态属性
static myName:string = "赵久燚"
// 定义方法
// 实例方法,通过实例进行使用
sayHello():void{
console.log("实例方法:hello!")
}
// 类方法,通过类名直接进行使用
static sayHi():void{
console.log("静态方法:hi!")
}
}
const per = new Person();
console.log("实例属性",per.name)
console.log("静态属性",Person.myName)
per.sayHello()
Person.sayHi()
// per.sex = "女" //报错
// 修改属性
per.age = 22
console.log(per.age)
构造函数
类的构造函数:constructor
只在实例化的时候被调用,并且只调用一次。
类的constructor
在类的外部无法访问。
class Dog{
name: string;
age: number;
// 构造函数 constructor,在对象创建时调用
constructor(name:string, age:number) {
this.name = name
this.age = age
}
dark(){
alert(`${this.name}在汪汪叫!`)
// 在方法中可以通过this指向当前实例本身
console.log(this)//打印当前实例
}
}
// 使用new关键字实例化类得到一个dog对象
const dog = new Dog("旺财",2);
// 调用对象方法
dog.dark() // 打印: 旺财在汪汪叫
继承
TypeScript 支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类
基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类
类继承使用关键字 extends
实例
// 定义一个animal公共类
class Animal{
// 构造函数的语法糖,在构造函数中添加修饰符,可省去`this.name = name`类似语法
constructor(public name: string, public age: number) {}
sayHello(){
console.log(`${this.name}:hello!`);
}
}
// 定义一个Sheep类继承animal类 , 此时Sheep为子类,animal为父类,子类拥有父类所有属性和方法
class Sheep extends Animal{
// 为sheep添加一个新的方法
eat(){
console.log(`${this.name}正在吃草`)
}
// 除了添加新的方法,我们还可以重写sayHello方法
sayHello() {
console.log(this.name,"咪咪咪")
}
}
class Cat extends Animal{}
let cat = new Cat("小猫",2);
let sheep = new Sheep("小阳",3);
cat.sayHello()
sheep.eat()
sheep.sayHello()
super
先定义一个父类
class Animal1{
name: string
constructor(name: string) {
this.name = name
}
sayHello(){
console.log("hello")
}
}
当我们想要继承这个个父类时,子类无法使用构造函数,就像这样
与前面继承的例子的不同点是,子类包含了一个构造函数,它 必须调用 super()
,它会执行基类的构造函数。 而且,在构造函数里访问 this
的属性之前,我们 一定要调用 super()
。 这个是TypeScript强制执行的一条重要规则。
// 在类的方法中,super表示当前类的父类
class Bird extends Animal1{
age: number
constructor(name: string,age: number) {
// 如果在子类中写了构造函数,此时子类的构造函数必须对父类的构造函数进行调用
super(name); // 调用父类的构造函数
this.age = age
}
sayHello() {
super.sayHello();
}
}
const bird = new Bird("小鸟",12);
抽象类
抽象类使用abstract
进行描述
抽象类与其他类的区别不大,但是只能用于继承,不能用来创建对象。
抽象类中可以定义抽象方法(抽象方法不能有方法体),子类必须对抽象方法进行重写。
定义一个抽象类
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
/*
* 定义一个抽象方法
* 抽象方法使用abstract关键字,没有方法体
* 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
* */
abstract eat():void;
}
定义一个类继承抽象类
class Dog extends Animal {
favoriteFood: string
constructor(name: string,favoriteFood: string) {
super(name);
this.favoriteFood = favoriteFood
}
// 重写抽象类中定义的抽象方法
eat(): void {
console.log(`${this.name}喜欢吃${this.favoriteFood}`)
}
}
let dog = new Dog2("小狗","火腿肠");
dog.eat() // 小狗喜欢吃火腿肠
dog.sayHello() //hello!
接口
接口可以帮我们定义一个类的结构
接口只定义对象的结构,不考虑实际值
接口中的所有属性都不能有实际的值
接口和抽象类类似,但是抽象类中可以添加抽象方法,而接口中默认全部都是抽象方法
在定义类时,可以使用implements
关键字对接口 进行实现
使用接口定义一个类结构
interface myInter{
name: string;
sayHello():void;
}
定义一个类用来实现接口
class MyClass implements myInter{
name: string;
constructor(name:string) {
this.name = name
}
sayHello(): void {
console.log(`hi~~~~${this.name}`)
}
}
可选属性
有时候,接口里的属性不一定全都是必需的,可以通过?
添加可选属性
interface myInter{
name?: string; // 此时,实现myInter接口的类可以选择不用实现name属性
sayHello():void;
}
可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。
属性封装
TS中可以添加属性的修饰符
public(不添加修饰符时,默认为public) 公共的,修饰的属性可以在任意位置修改,包括子类
private 私有属性,只能在类内部进行访问(修改)
protected 受保护的类,只能在当前类和当前类的子类中使用
getter和setter方法为属性的存取器,可以通过get 和 set 方法修改或获取属性值
通过get set方法可以控制属性的合法性。
实例
class Person {
private _name: string;
private _age: number;
get age(): number {
return this._age;
}
set age(value: number) {
// 控制年龄合法性
if (value > 0){
this._age = value;
}
}
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
}
const person = new Person("孙悟空",21);
// person2.name = '猪八戒' // 报错
person.name = "猪八戒"
person.age = -123 // 年龄不合法,不能修改
console.log(person);
泛型
在定义函数或类时,如果遇到类型不明确时可以使用泛型
泛型是一种特殊的变量,用来表示类型,而不是值
// 这里的T就是一个泛型
function fn<T>(a:T): T{
console.log(typeof a);
return a
}
// 不指定泛型,TS自动对类型进行推断
fn("string")
// 手动指定泛型
fn<number>(10)
还可以指定多个泛型
function f<U,T>(num:U,str:T):U {
return num
}
f<number,string>(10,"20")
限定泛型的范围,定义一个接口来描述约束条件
interface Inter {
length: number;
}
// 表示泛型T必须是Inter的一个实现类、子类
function f1<T extends Inter>(a:T):number {
return a.length
}
//f1(123) //报错,应为数值类型没有length属性
f1('123')
在类中也可以使用泛型
class MyClass<T>{
name: T;
constructor(name:T) {
this.name = name
}
}
const mc = new MyClass<string>("孙悟空")