1.TypeScript是什么?
- 以JavaScript为基础构建的语言
- 是JavaScript的超集
- 可以在任何支持JavaScript的平台中执行
- TypeScript扩展了JavaScript,并添加了类型
- TS不能被JS解析器直接执行,需要将TS编译成JS
2.TypeScript开发环境搭建
1.下载并安装Node.js
2.使用npm全局安装typescript
npm i -g typescript
3.创建一个ts文件
4.使用tsc对ts文件进行编译
- 进入命令行
- 进入ts文件所在目录
- 执行命令:tsc xxx.ts
3. 基本类型
1.类型声明
- 类型声明是TS非常重要的一个特点
- 通过类型声明可以指定TS中变量(参数、形参)的类型
- 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则会报错
- 简言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
2.自动类型判断
- TS拥有自动的类型判断机制
- 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
- 所以当变量的声明和赋值同时进行,可以省略掉类型声明
3.类型
类型 | 例子 | 描述 |
number | 1,-3,2.5 | 任意数字 |
string | ’hello‘,hello | 任意字符串 |
boolean | true 、 false | 布尔值 |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值(undefined) | 没有值(或undefined) |
never | 没有值 | 不能是任何值 |
object | {name:'佩奇'} | 任意js对象 |
array | [1,2,3] | 任意js数组 |
tuple | [4,5] | 元素,TS新增类型,固定长度的数组 |
enum | enum{A,B} | 枚举,TS中新增类型 |
//声明一个变量a,同时指定它的类型为number
let a: number;
//a的类型设置为了number,在以后使用的过程中a的值只能是数字
a = 10;
a = 33;
// a = "hello"; //此行代码报错
let b: string;
b = "hello";
// b = 123; //此行代码报错
let c = false;//变量的声明和赋值同时进行,可以省略类型说明,TS会自动进行类型检测
c = true;
// c = 123;报错
//在JS中,函数不考虑参数的类型和个数,TS中略有不同
function sum(a, b) {
return a + b;
}
sum(123, 456)//579
sum(123,'456')//'123456'
//规定了sum2参数类型和返回值类型都是number
function sum2(a:number, b:number):number {
// return a + "hello" 返回值类型错误
return a + b;
}
sum2(123, 456) //579
// sum2(123,'456')报错,类型
// sum2(123,456,789)报错,个数
let d: 10; //使用字面量进行类型声明,类似于常量,只能是10,不能改变
d = 10;
// d = 11; 报错
// 可以使用 | 来连接多个类型(联合类型)
let e: "male" | "female";
e = "male";
e = "female";
let f: boolean | string; //联合类型
f = true;
f = "hello"
//any表示的是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测
//使用TS时,不建议使用any类型
//let g:any (显式的任意类型)
//声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any(隐式的any)
let g;
g = true;
g = 10;
g = "hello"
//unknown 表示未知类型的值
let h: unknown;
h = 10;
h = "hello";
h = false;
//但是any和unknown是有区别的
let i: string;
i = g //g的类型是any,它可以赋值给任意变量
h = "hi"//上面已经规定h为unknown类型
//unknown实际上就是一个类型安全的any,unknown类型的变量不能直接赋值给其他变量
// i = h 报错
//但是可以提前判断
if (typeof h === 'string') {
i = h;
}
//还可以类型断言:用来告诉解析器变量的实际类型
//此处是告诉解析器 h就是字符串类型 (以下两种写法均可)
/*
类型断言语法
变量 as 类型
<类型>变量
*/
i = h as string;
i = <string>h
//void表示空,以函数为例,就表示没有返回值的函数
function fn():void{
//return 123 报错
//return "hello" 报错
//return 可
//return undefined 可
//return null 可
//没有return,可
}
//never表示永远不会返回结果
function fn2(): never{
}
function fn3(): never{
throw new Error("报错了")//永远不会返回结果
}
//object表示一个js对象
let ab: object;
ab = {};
ab = function () { };//函数也是object
//{}用来指定对象中可以包含哪些属性
//语法:{属性名:属性值,属性名:属性值}
let ac: { name: string }
// ac = {} 报错
// ac = {name:'佩奇',age:10} 报错,结构必须与指定的一样,多了少了都不行
ac = { name: '佩奇' }
//在属性名后边加上?,表示属性是可选的
let ad: { name: string, age?: number }
ad = { name: '佩奇', age:10}
ad = { name: '佩奇' }
//[propName: string]: any 表示任意类型的属性 propName可更改成其他字段,非固定写法
let ae: { name: string, [propName: string]: any }
ae = { name: '猪八戒', age: 10, gender: '男' }
//设置函数结构的类型声明:
//语法:(形参:类型,形参:类型...)=>返回值
let af: (a: number, b: number) => number
af = function (n1: number, n2: number): number {
return n1 + n2
}
//数组的类型声明 1.类型[] 2.Array<类型>
//string[] 表示字符串数组
let ag: string[]
ag = ['a','b']
//number[] 表示数值数组
let ah: number[]
ah = [1, 2, 3]
//Array<number> 也表示数值数组
let aj: Array<number>
aj = [1, 2, 3]
// 元组:固定长度的数组
//语法 [类型,类型,类型]
let ak: [string, string]
ak = ["li", "wang"]
// enum 枚举
enum Gender{
male,
female
}
let bb: { name: string, gender: Gender }
bb = { name: "猪八戒", gender: Gender.male }
console.log(bb.gender == Gender.male) //true
//& 表示“且”
let bc: { name: string } & { age: number }
bc = { name: '佩奇', age: 10 }
//类型的别名
type myType = 1 | 2 | 3 | 4 | 5
let bd: myType
bd=3
4.编译选项
1.自动编译文件
编译文件时,使用-w指令后,TS编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译。
tsc xxx.ts -w
2.自动编译整个项目
1.在项目根目录下创建ts的配置文件tsconfig.json
可以使用命令tsc -init
2.添加配置文件后,只需要直接使用tsc命令即可完成对整个项目的编译
3.配置选项
{
/*
ts编译器会根据tsconfig.json的配置对代码进行编译
"include"用来指定哪些ts文件需要被编译
路径:** 表示任意目录
* 表示任意文件
"exclude"不需要被编译的文件目录
默认值:['node_modules','bower_components','jspm_packages']
*/
"include":[
"./src/**/*"
],
"exclude":[
"./src/hello/**/*"
],
//编译器的选项
"compilerOptions":{
//target用来指定ts被编译为的ES的版本
"target": "es2016",
//lib用来指定项目中要使用的库
// "lib": [],
// module指定要使用的模块化规范
"module": "commonjs",
//outDir用来指定编译后文件所在的目录
// "outDir": "./",
//设置outFile后,所有的额全局作用域中的代码会合并为一个文件
//所有严格检查的总开关,为true时,其他严格检查自动开启,为false其他严格检查自动关闭
"strict": true,
// "outFile": "./app.js",
//是否对js文件进行编译,默认为false
// "allowJs": true,
//是否检查js代码是否符合语法规范,默认为false
// "checkJs": false,
//是否移除注释,默认为false
// "removeComments": true,
//不生成编译后的文件,默认为false
// "noEmit": true,
// 当有错误时不生成编译后的文件
// "noEmitOnError": true,
//用来设置编译后的文件是否使用严格模式,默认为false
// "alwaysStrict": true,
//不允许隐式的any类型
// "noImplicitAny": true,
//不允许不明确类型的this
// "noImplicitThis": true,
//严格的检查空值
// "strictNullChecks": true,
}
}
5.类
1.类的简介
使用class关键字来定义一个类
对象中主要包含两部分:属性和方法
class Person{
/*
直接定义的属性是实例属性,需要通过对象的实例去访问
const per = new Person()
per.name
使用static开头的属性是静态属性(类属性),可以直接通过类访问
Person.age
readonly开头的属性表示一个只读的属性 无法修改
*/
//定义实例属性
name = "贾宝玉"
//在属性前使用static关键字可以定义类属性(静态属性)
static age = 18
readonly job = 'teacher'
//定义方法
//如果方法以static开头则方法就是类方法,可以直接通过类去调用
sayHello(){
console.log('Hello!')
}
}
const per = new Person();
console.log(per)
2.构造函数和this:
class Dog{
name:string;
age:number;
//constructor被称为构造函数
//构造函数会在对象创建时调用
constructor(name:string,age:number){
//在实例方法中,this就表示当前的实例
//在构造函数中当前对象就是当前新建的那个对象,可以通过this向新建的对象中添加属性
this.name = name
this.age = age
}
bark(){
alert('汪汪汪!')
//在方法中可以通过this来表示当前调用方法的对象,谁调用,this就是谁
console.log(this);
}
}
const dog1 = new Dog('小黑',10)
const dog2 = new Dog('小白',2)
dog2.bark()//bark里的this为dog2
3.继承
//继承
class Animal{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name
this.age = age
};
sayHello(){
console.log('动物在叫!');
}
}
/**
* Cat extends Animal
* -此时,Animal被称为父类,Cat被称为子类
* -使用继承后,子类将会拥有父类所有的方法和属性
* -通过继承可以将多个类中共有的代码写在一个父类中,这样只需写一次即可让所有的子类都同时拥有父类中的属性和方法
* -如果希望在子类中添加一些父类中没有的属性或方法,直接加就行
* -如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类方法(方法重写)
*/
class Cat extends Animal{
eat(){
console.log(`${this.name}吃鱼`);
}
}
class Pig extends Animal{
color:string;
//如果在子类中写了构造函数,在子类的构造函数中必须对父类的构造函数进行调用 用super
constructor(name:string,age:number,color:string){
super(name,age)//调用父类的构造函数
this.color = color
}
sayHello(){
//在类的方法中 super就表示当前类的父类 super.sayHello()调用的是父类的函数
super.sayHello()
}
}
const cat = new Cat('小花猫',2)
const pig = new Pig('佩奇',3)
cat.eat()
4.抽象类
//抽象类
/**
* 以abstract开头的类是抽象类
* 抽象类和其他类区别不大,只是不能用来创建对象,而是专门用来被继承的类
* 抽象类中可以添加抽象方法
*/
abstract class Car{
name:string;
constructor(name){
this.name = name
}
/**
* 定义一个抽象方法
* 抽象方法使用abstract开头,没有方法体
* 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
*/
abstract run()
}
class BenChi extends Car{
run() {
console.log('奔驰跑起来了');
}
}
const car = new Car('车')//报错
6.接口
接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
/**
* 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法,
* 同时接口也可以当成类型声明去使用
*/
//类型别名,重复声明会报错
type newType = {
name:string,
age:number
}
type newType = {
gender:string,
}
//接口,重复声明会合并,相当于myInterface里定义了三个属性
interface myInterface{
name:string,
age:number
}
interface myInterface{
gender:string,
}
const obj:myInterface = {
name:'佩奇',
age:1,
gender:'男',
}
/**
* 接口可以在定义类的时候去限制类的结构
* 接口中所有的属性都不能有实际的值
* 接口只定义对象的结构,而不考虑实际值
* 接口中的所有方法都是抽象方法
*/
interface myInter{
name:string,
sayHello():void
}
/*定义类时,可以使类去实现一个接口
实现接口就是使类满足接口的要求
*/
class Myclass implements myInter{
name: string;
constructor(name: string){
this.name = name
}
sayHello(){
console.log('大家好');
}
}
7.属性的封装
class Person{
/**
* public 修饰的属性可以在任意位置访问(修改)默认值 包括子类
* private 私有属性,私有属性只能在类内部进行访问(修改)
* -通过在类中添加方法使得私有属性可以被外部访问
* protected 受保护的属性 只能在当前类和当前类的子类中访问(修改)
*/
// private name:string;
// private age:number;
_name:string;
_age:number;
constructor(name:string,age:number){
this._name = name
this._age = age
}
/**
* 现在属性是在对象中设置的,可以任意被修改,导致对象中的数据变得很不安全
*/
/**
* getter方法用来读取属性
* setter方法用来设置属性
* -他们被称为属性的存取器
*/
//下面这种要通过调函数的方式per.getAge() per.setAge(10)
getAge(){
return this._age
};
setAge(value:number){
if(value>=0){
this._age = value
}
}
//TS中设置getter setter的方式 可以直接.name来读取和调用 per.name = '猪八戒'
get name(){
return this._name
}
set name(value){
this._name = value
}
}
const per = new Person('孙悟空',12)
// per.setAge(10)
per.name = '猪八戒'
class A{
// num:number;//默认的修饰符为public
// private num2:number;
protected num3:number;
constructor(num){
this.num = num
this.num2 = this.num2
this.num3 = this.num3
}
}
class B extends A{
test(){
// console.log(this.num);//可以访问
// console.log(this.num2);//不能访问
console.log(this.num3);//可以访问
}
}
const b = new B(1)
console.log(b.num3);//不能访问
class C{
//可以直接将属性定义在构造函数中
constructor(public name:string,public age:number){
}
}
const c = new C('孙悟空',10)
8.泛型
//泛型
function fnn<T>(a:T):T{
return a
}
fnn(10) //不指定泛型,TS可以自动对类型进行推断
fnn('hello') //指定泛型
//泛型可以同时指定多个
function fnn2<T,K>(a:T,b:K):T{
console.log(b);
return a
}
fnn2<number,string>(10,'hello')
//定义一个接口
interface Inter{
length:number
}
//T extends Inter 表示泛型T必须是Inter实现类(子类)
function fnn3<T extends Inter>(a:T):number{
return a.length
}
class MyClass<T>{
name:T;
constructor(name:T){
this.name = name
}
}
const mc = new MyClass<string>('孙悟空')