TS简介
TypeScript 是一种由微软开发的自由开源的编程语言,他是JavaScript的一个超集,扩展了JavaScript的语法,主要提供了类型系统和对 ES6 的支持。
TypeScript 设计目标是开发大型应用,它可以编译成纯JavaScript,编译出来的JavaScript 可以运行在任何浏览器上。
- TypeScript 是JavaScript 的超集,扩展了JavaScript 的语法,因此现有的JavaScript 代码可与TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。
- TypeScript 可处理已有的JavaScript 代码,并只对其中的 TypeScript 代码进行编译。
TypeScript增加了什么?
- 类型
- 添加ES不具备的新特性
- 支持ES的新特性
- 丰富的配置选项
- 强大的开发工具
TypeScript 的优势
强大的IDE支持:体现在三个特性上:
1.类型检查,在TS中允许你为变量指定类型。
2.语法提示。
3.重构。 Angular2、vue3的开发语言
TypeScript 的缺点
有一定的学习成本,需要理解接口 (Interfaces)泛型 (Generics) 、类(Classes) 、枚举类型(Enums)等前端开发可能不是很熟悉的知识点
TypeScript 用法
使用: 指定变量的类型,: 的前后有没有空格都可以
window.onload=function(){
let num:number = 15;
num(变量名) :number(类型) = 15 //(具体值)
//表示定义一个变量num,指定类型为number
let str:string = 'abc';
//表示定义一个变量str,指定类型为string;
let flag:boolean = false; //布尔类型
let num:number = 15; //数值类型
let str:string ='abc'; //字符串类型
let str2:string = hello,$(str] ;
let msg:string = `hello,$(str],$(num)`;
let u: undefined = undefined;
let n: null = null;
//联合类型
let x: number | string = '10';
x = 10;
//任意类型
let y: any = true;
y = 10;
y ='hello';
//引用类型 数组 对象 函数
//数组 类型+方括号
let arr1:number[ ] = [1,2,3,4,5];
let arr2:string[ ] = ['a','b','c'];
let arr3:(numberlstring)[] = [1,2,'a' 'b']; //联合类型
let arr4:any[] = [1,2, 'a',true];
//数组泛型
let arr5:Array<number>= [12,3,4,5];
let arr6:Array<string> = ['a','b','c'];
let arr7:Array<number|string> = [1,2,'a','b'];
}
接口 (nterfaces)
在面向对象语言中,接口 (nterfaces)是对行为的抽象接口 (Interfaces) 可以用于对[对象的形状 (Shape) ] 进行描述。
window.onload=function(){
interface IObj {
//接口首字母大写 I
id:number; //确定属性
title?:string; //可选属性
[proName:string]:any; //任意属性
}
//①
let o1:IObj = {
//定义的变量比接口少了一些属性是不允许的
id:10,
title:'o1',
fn:function(){
//可有方法
},
a:'1',
b:'2'
};
//② 数组对象
interface IList {
id:number;
text:string;
}
let lists:IList[] = [
{id:1,text:'1'},
{id:2,text:'2'},
(id:3,text:'3'},
]
let lists2:Array<IList> = [ //泛型数组
{id:1,text:'1'},
{id:2,text:'2'},
{id:3 , text:'3'},
]
//③ 函数 输入类型 输出类型
function sum(n1:number,n2:number):number{
return n1+n2;
};
sum(10,20);
function sum(n1:number,n2:string):(number|string)[]
return [n1,n2]
};
sum(10,'20');
//void 无返回值
function fn(n1:number,n2 ?: number):void{
//
};
fn(10,5)
}
泛型
(Gnerics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
//泛型 泛型变量T T表示任何类型
function sum5<T>(n1:T,n2:T):T[]{
return [n1,n2]
}
sum5<number>(10,20);
sum5<string>('a','b');//类型推断
sum5(true,false);
//T 泛型约束 T extends ILength
interface ILength{
length:number;
}
function fn2<T extends ILength>(v:T):number{
//extends 表示约束
return v.length //v的类型是任何类型 并非所有类型都有长度
};
fn2<string>('hello');
//字符串是一种对象类型,每个字符串都是一个实例化的对象。由于字符串是一种对象,所以它们具有属性和方法。
//fn2<number>(10); //number没有长度
fn2<number[]>([1,2,3]);
//🌟接口 + 泛型 的应用
//定义一个函数,判断形参是否一样
let f1 = function(n1,n2) [
return n1===n2
};
f1(18,20);
//添加类型约束
//let f2 = function(n1:number,n2:number):boolean { 改为用接口
let f2:IinspectFn = function(n1,n2){
return n1===n2
};
f2(10,20);
//定义另一个函数
let f3 = function(x,y)(
if(x>y){
return true;
}else (
return false;
}
};
f3(10,20)
//添加类型约束
//let f4 = function(x:number,y:number ):boolean{
let f4:IinspectFn= function(x,y){
if(x>y){
return true;
}else{
return false;
}
};
f4(10,20)
let f4:IinspectFn<string> = function(x,y)
if(x>y){
return true;
}else{
return false;
}
};
f4('10','20')
//接口
interface IinspectFn{
(a:number b:number):boolean;
//书写情况会不一样
};
interface IinspectFn<T>(
(a:T,b:T) :boolean;
};
基本类型
①类型声明
- 类型声明是TS非常重要的一个特点
- 通过类型声明可以指定TS中变量 (参数、形参)的类型
- 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错。简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
//① 声明一个交量a,同时指定它的类型为number
let a: number;
// a 的类型设置为了number,在以后的使用过程中a的值只能是数字
a = 10;
a = 33;
//a ='hello'; // 此行代码会报错,因为交量a的类型是number,不能赋值字符串
// ② 声明完交量直接进行赋值
// let c: boolean = false;
// 如果变量的声明和赋值是同时进行的,TS 可以自动对交量进行类型检测
let c = false;
c = true;
//③JS 中的函数是不考店参数的类型和个数的
function sum(a,b){
return a + b;
}
console.log(sum(123,456)); // 579
console.log(sum(123,"456"));
//"123456"
//方法
function sum(a: number, b: number): number{ //约束返回值类型
return a + b;
}
let result = sum( a: 123, b: 456);
- let 变量:类型;
- let 变量: 类型 = 值;
- function fn(参数:类型参数:类型):类型{ ··· }
②自动类型判断
- TS拥有自动的类型判断机制
- 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
- 所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明
类型:
类型 | 例子 | 描述 |
number | 1, -33, 2.5 | 任意数字 |
string | 'hi', "hi", hi | 任意字符串 |
boolean | true、false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值 (undefined) | 没有值 (或undefined) |
never | 没有值 | 不能是任何值 |
object | {name:'孙悟空'} | 任意的]S对象 |
array | [1,2,3] | 任意JS数组 |
tuple | [4,5] | 元素,TS新增类型,固定长度数组 |
enum | enum(A, B) | 枚举,TS中新增类型 |
// ④ 也可以直接使用字面量进行类型声明
let a: 10;
a= 10;
// ⑤ 可以使用 | 来连接多个类型(联合类型) 或的意思
let b: "male" | "female";
b = "male";
b ="female";
let c: boolean | string;
c = true;
c ='hello';
// ⑥ any 表示的是任意类型,一个变量设置类型为any 后相当于对该变量关闭了TS的类型检测
// 使用TS时,不建议使用any美型
// let d: any;
//声明交量如果不指定类型,则TS 解析器会自动判断变量的美型为any(隐式的any )
let d;
d = 10;
d = 'hello';
d = true;
// unknown 表示未知类型的值
let e: unknown;
e = 10;
e ="hello";
e = true;
let s:string;
// d的类型是any,它可以赋值给任意交量
// s = d;
e = 'hello';
// unknown 实际上就是一个类型安全的any
//unknown美型的交量,不能直接赋值给其他交量
if(typeof e === "string"){ s =e }
// 类型断言,可以用来告诉解析器变量的实际类型
//语法: (1)变量 as 类型 (2)<类型>变量
s = e as string;
s = <string>e;
// ⑦ void 用来表示空,以函数为例,就表示没有返回值的函数
function fn(): void {
return null;
return undefined;
}
// never 表示永远不会返回结果
function fn( ): never{
throw new Error('报错了!');
}
// ⑧ object表示一个js对象
let a: object;
a={};
a = function () { };
// { } 用来指定对象中可以包含哪些属性
// 语法: 属性名: 属性值,性名: 性值
// 在属性名后边加 上?,表示属性是可选的
let b: {name: string, age?: number};
b = {name:'孙悟空',age: 18};
// [propName: string]: any 属性名为字符串 表示任意类型的属性
let c: {name: string, [propName: string]: any};
c = {name:'猪八戒',age: 18,gender:'男'};
设置函数结构的类型声明
语法:(形参:类型,形参: 类型...) => 返回值
let d: (a: number ,b: number)=>number;
// d = function (n1: string,n2: string): number{
return 10;
}
//⑨ tuple 元组,元组就是固定长度的数组 语法:[类型,类型,类型]
let h: [string, number];
h = ['hello',123];
enum Gender{
Male =0,
Female =1
}
let i: {name: string, gender: Gender};
i = {
name: '孙悟空',
gender: Gender.Male
}
console.log(i.gender === Gender.Male);
// &表示同时
let j: { name: string } & { age: number };
// j = {name:'孙悟空', age: 18};
// 类型的别名
type myType = 1 | 2 | 3 | 4 | 5;
let k: myType;
let l: myType;
编译选项
自动编译文件
- 编译文件时,使用 -w 指令后,TS编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译.
- 示例: tsc xxx.ts -w
tsconfig.json是ts编译器的配置文件,ts 编译器可以根据它的信息来对代码进行编译
配置选项:
(1)include
"incLude" 用来指定哪些ts 文件需要被编译
定义希望被编译文件所在的目录
默认值:["**/*"] 路径: ** 表示任意目录 *表示任意文件
示例:
- "include":[./src/**/*"],
- 上述示例中,所有src目录下的文件都会被编译
(2)exclude
定义需要排除在外的目录
默认值:[“node_modules"”,"bower_components","jspm_packages"]
示例:
- "exclude":["./src/hello/**/*"]
- 上述示例中,src下hello目录下的文件都不会被编译
(3)extends
定义被继承的配置文件
示例:
- "extends":". /configs /base'
- 上述示例中,当前配置文件中会自动包含config目录下base.ison中的所有配置信息
(4)files
指定被编译文件的列表,只有需要编译的文件少时才会用到
示例:
"files":[
"core.ts",
"checker .ts",
"tsc.ts"
]
- 列表中的文件都会被TS编译器所编译
(5)compilerOptions
- 编译选项是配置文件中非常重要也比较复杂的配置选项
- 在compilerOptions中包含多个子选项,用来完成对编译的配置
项目选项
1️⃣target
设置ts代码编译的目标版本
可选值: ES3 (默认)、ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext(最新版本)
示例: 所编写的ts代码将会被编译为ES6版本的JS代码
"compilerOptions":{
"target":"ES6"
}
2️⃣lib
指定代码运行时所包含的库(宿主环境)
可选值:
ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext、DOM、WebWorker、ScriptHost ......
示例:
"compilerOptions":{
"target":"ES6",
"lib":["ES6","DOM"],
"outDir":"dist", // outDir 用来指定编译后文件所在的目录
"outFile":"dist/aa.js" // 将代码合并为一个文件
//设置outFile 后,所有的全局作用域中的代码会合并到同一个文件中
//module 指定要使用的模块化的规范
// 是否对js文件进行编译,默认是false
"allowJs": true,
//是否检台js 代码是否符合语法规范,默认是false
"checkIs":true,
// 是否移除注释
"removeComments": true,
// 不生成编译后的文件
"noEmit": false,
// 当有错误时不生成编译后的文件
"noEmitOnError": true,
// 所有严格检查的总开关——写后以下的代码都可以不用写
"strict":true,
// 用来设置编译后的文件是否使用,严格模式,默认false
"alwaysStrict": false,
// 不允许隐式的any类型
"noImplicitAny": true,
//不允许不明确类型的this
"noImplicitThis": true,
// 严格的检查空值
"strictNuliChecks": true,
}
面向对象
在程序中所有的对象都被分成了两个部分数据和功能,以人为例,人的姓名、性别等属于数据,人可以说话、走路这些属于人的功能。数据在对象中被成为属性,而功能就被称为方法。所以简而言之,在程序中一切皆是对象。
1、类(class)
要想面向对象,操作对象,首先便要拥有对象,那么下一个问题就是如何创建对象。要创建对象,必须要先定义类,所谓的类可以理解为对象的模型,程席中可以根据类创建指定类型的对象,举例来说:可以通过Person类来创建人的对象,通过D类创建狗的对象,通过Car类来创建汽车的对象,不同的类可以用来创建不同的对象。
定义类:
class 类名 {
属性名:类型;
constructor(参数: 类型){
this.属性名 = 参数;
}
方法名( ){
···
}
}
readonly 开头的属性表示一个只读的属性无法修改
class Person {
// 定义实例属性
name: string = '孙悟空';
// 在属性前使用static 关键字可以定义类属性(静态属性)
static age: number = 18;
}
①直接定义的属性是实例属性,需要通过对象的实例去访问:
const per = new Person( ); //创建对象,per是实例
per.name
②使用static开头的属性是静态属性(类属性),可以直接通过类去访问
- Person.age
定义方法
如果方法以 static 开头则方法就是类方法,可以直接通过类去调用
sayHello(){
console.log('Hello 大家好! );
}
class Dog{
name: string;
age: number;
//在类中先定义属性,否则下面的使用会报错
// constructor 被称为构造函数
//构造函数在对象创建时调用
constructor(name: string, age: number) {
//在实例法中,this就表示当前当前的实例
// 在构造西数中当前对象就是当前新建的那个对象
// 在构造函数中 可以通过this 向新建的对象中添加属性
this.name = name;
this.age = age;
bark(){
// 在方法中 可以通过this来表示当前调用方法的对象
console.log(this.name);
}
}
🌟立即执行函数 作用:创建一个独立的作用域,防止变量污染
- 声明一个匿名函数
- 马上调用这个函数
(function(){
···
})( )
//不加一对小括号,写成如下形式:——> 为了兼容js语法
function( ){alert('我是匿名函数')}( )
Dog extends Animal
- 此时,Animal被称为父类,Dog 被称为子类
- 使用继承后,于类将会拥有父类所有的方法和属性
- 通过继承可以将多个类中共有的代码写在一个父类中,这样只需要写一次即可让所有的子类都同时拥有父类中的属性与方法
- 如果希望在子类中添加一些父类中没有的属性或方法直接加就行
- 如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的方法,这种子类覆盖掉父类方法的形式,称为方法重写
- 如果在子类中写了构造函数,在子类构造函数中必须对父类的构造函数进行调用 super( )
super(父类属性); // 调用父类的构造函数
以abstract开头的类是抽象类,
- 抽象类和其他类区别不大,只是不能用来创建对象
- 抽象类就是专门用来被继承的类
- 抽象类中可以添加抽象方法
abstract class Animal {
// 定义一个抽象方法
// 抽象方法使用 abstract开头,没有方法体
// 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
abstract sayHello():void;
}
接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法,同时接口也可以当成类型声明去使用
(function(){
// 描述一个对象的类型
type myType = {
name: string,
age: number
};
interface myInterface {
name: string;
age: number;
}
interface myInterface {
gender: string;
}
const obj: myInterface = {
name: 'sss'
age: 111,
gender:'男'
}
//接口可以在定义类的时候去限制类的结构
//接口中的所有的属性都不能有实际的值接口只定义对象的结构,而不考虑实际值
//在接口中所有的方法都是抽象方法
//定义类时,可以使类去实现一个接口,实现接口就是使类满足接口的要求
class MyClass implements myInter{
name: string;
constructor(name: string) {
this .name = name;
}
sayHello(){
console.Log('大家好~~');
}
}
})( )
2、属性封装
现在属性是在对象中设置的,属性可以任意的被修改,属性可以任意被修改将会导致对象中的数据变得非常不安全
TS可以在属性前添加属性的修饰符
public 修饰的属性可以在任意位置访问(修改) 默认值
private 私有属性,私有属性只能在类内部进行访问(修改)
- 通过在类中添加方法使得私有属性可以被外部访同
- getter方法用来读取属性,setter方法用来设置属性,它们被称为属性的存取器
// TS中设置getter方法的方式
get name(){
return this._name;
}
set name(value : string ){
this._name = value;
}
protected 受包含的属性,只能在当前类和当前类的子类中访问(修改)
class C{
// 可以直接将属性定义在构造函数中
constructor(public name: string, public age: number){}
}
在定义函数或是类时,如果遇到类型不明确就可以使用泛型
(习惯上 T是type K是key V是value E是枚举)
function fn<T>(a: T): T{
return a;
}
//可以直接调用具有泛型的函数
fn( a: 10); //不指定泛型,TS 可以自动对类型进行推断
fn<string>( a:'hello'); // 指定泛型
//泛型可以同时指定多个
function fn2<T,K>(a: T,b: K):T{
console.log(b);
return a;
}
fn2<number, string>( a: 123, b: 'hello');
interface Inter{
length: number;
}
// T extends Inter 表示泛型必须时Inter实现类(子类)
function fn3<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>( name:'孙悟空');