一、初始TypeScript
①js的超集
_包含js的所有元素
_能运行js代码
_支持ES语法
_是一种开源、跨平台的编程语言
②编译器编译为js代码,js解析器执行
③完全兼容js代码
④静态类型检查器
_静态类型,更加严格的语法
_减少运行时异常出现的几率
_后期维护
二、第一个TypeScript程序
①安装TypeScript(看官方文档来)
npm install -g typescript //全局安装
tsc -v //查看版本号
②编写代码
(()=>{
function sayHi(str:string){
return '你好'+str
}
sayHi('123')
let a:Number=30
console.log(a)
})()
③手动编译为js文件(不推荐)
终端输入: tsc 文件名
④vscode自动编译(推荐)
1.终端里:tsc --init //生成配置文件
2.修改配置文件 tsconfig.json : "outDir":"./js" (导出的js文件路径) & "strict":false (是否开启严格模式)
3.启动监视任务:终端 => 运行任务=>监视tsconfig.json
三、类型声明
// 类型声明 指定ts变量(参数,形参)的类型 ts编译器,自动检查
// 类型声明给变量设置了类型,使用变量只能存储某种类型的值
①基础类型:基础类主要有 布尔类型、数字类型、字符串类型、undefined和null
_基础类型的声明如下代码所示,注意:undefined和null可以作为其它类型的子类型
// 布尔类型 boolean
let flag: boolean = true
// flag=123 报错
flag = false
// 数字类型 number
let a:number = 10 //十进制
let a1:number = 0b1010//二进制
let a2:number=0o12 //八进制
let a3:number=0xa //十六进制
a = 11
// 字符串类型 string
let str:string='123'
// str=123
str=''
// undefined和null, 用的不多
let u:undefined=undefined
let n:null=null
console.log(u);
console.log(n);
// u=123
// undefined和null 还可以作为其他类型的子类型
// 可以把undefined和null 赋值给其他类型的变量
let b:number=undefined
let str1:string=null
console.log(b);
console.log(str1);
②数组:
*声明方式有两种:普通声明和泛型(后面会讲)
// 定义数组一:
let arr1:number[]=[1,2,3]
arr1=[1,2,3]
// 定义数组二:泛型
let arr2:Array<number>=[10,20,30]
③对象
*表示非原始类型,除了number、string、boolean之外的类型
*let obj:object={}
// object 表示非原始类型,除了number、string、boolean之外的类型
let obj:object={}
// 字面量
// obj=123 报错
// obj='' 报错
obj=null
obj=undefined
obj=[]
obj=new String()
obj=String
④any(任何类型)
有优点也有缺点
// any 任何类型
let h:any=123
h=true
h="123"
h={}
h=[]
let newArr:any[]=[100,2,4,"",true]
// 有优点也有缺点
console.log(newArr[0].split(''));//数字类型没有这个方法,这里不报错,但是控制台会报错
⑤void
空值,常用于表示没有任何返回值的函数
// void空值 ,表示没有任何返回值的函数
function fun1(): void {
console.log(123);
// return undefined
}
console.log(fun1());
// 没有任何类型
let v: void = undefined
⑥类型推断
* ts在没有明确的指定类型的时候推测一个类型
1、定义变量的时候,直接给变量赋值,则定义类型为对应的类型
2、定义变量的时候,没有赋值,则定义类型为any类型
// ts在没有明确的指定类型的时候推测一个类型
// 2种情况:
// 1、定义变量的时候,直接给变量赋值,则定义类型为对应的类型
// 2、定义变量的时候,没有赋值,则定义类型为any类型
let t = 123;//将t定义一个number类型通过类型推断
// t=""报错
let g;
g = 123
g = ""
g = []
g = {}
⑦联合类型
* 表示取值可以为多种类型中的一种
* 只能访问此联合类型的所有类型里共有的属性或方法
* 再次赋值,走类型推断,给变量定义一个类型
// 表示取值可以为多种类型中的一种
// flag true,1 false,0 ||
let f: boolean | number | string = true
// 只能访问此联合类型的所有类型里共有的属性或方法
// f = 123 //再次赋值,走类型推断,给变量定义一个类型
f="123"
// f=true
console.log(f.toString());
四、接口
定义:它是对行为的抽象,用于对「对象的形状(Shape)」进行描述,理解为一种约束,使用interface定义
①对象类型
1. 定义的变量比接口少了一些属性是不允许的,不能多出其他的属性
2. ? 表示可选属性
3. readonly 定义只读属性
// 它是对行为的抽象,用于对「对象的形状(Shape)」进行描述,理解为一种约束
// 接口一般首字母大写 ,
// 定义的变量比接口少了一些属性是不允许的,不能多出其他的属性
// ? 表示可选属性,定义对象 ? 的属性可有可无
// [propName:string]:any 任意属性和任意属性值
// [propName:string]:string 需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
// 一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型
// 可以用 readonly 定义只读属性
// 定义接口
interface IPerson {
readonly id:number,
name: string,
age: number,
sex?: string,
// [propName:string]:any
[propName: string]: string | number | boolean
}
let p: IPerson = {
id:10,
name: "张三",
age: 18,
// sex:"男",
width: "123",//报错
}
// p.id=11
②数组类型
// 不常用,理解
interface INewArray{
[index:number]:number//任意属性,index表示数组中的下标
}
// [1,2,3,4] arr[0]-->obj['name']
// 0,1,2,3
let arr:INewArray=[1,2,3,4]
③函数类型
interface ISearchFunc {
// (参数:类型,....):返回值的类型
(a: string, b: string): boolean
}
// 参数,返回值
const fun1: ISearchFunc = function (a: string, b: string): boolean {
return a.search(b) !== -1
}
console.log(fun1('123','1'));
五、函数
①函数声明和函数表达式
* 和js写完差不多,就是多了声明类型,还有完整写法
// //js 函数声明,命名函数
// function add(a,b){
// return a+b
// }
// // 函数表达式,匿名函数
// let add2=function(a,b){
// return a+b
// }
// ts 函数声明,命名函数
// a 和 b 都是number类型
// : number 表示该函数的返回值为number类型
function add(a: number, b: number): number {
return a + b
}
console.log(add(1, 2));
let c: number = add(1, 2)
console.log(c);
// 函数表达式,匿名函数
let add2 = function (a: number, b: number): number {
return a + b
}
console.log(add2(1,2));
// 函数完整的写法
let add3:(a:number,b:number)=>number=function(a: number, b: number): number {
return a + b
}
②可选参数和默认参数
* 可选参数 ?
* 默认参数 赋值
* 可选参数不能放在必选参数之前
let getName = function (x: string, y?: string, z: string = "你好"): string {
return x + y + z
}
// 可选参数 ? 必选参数不能位于可选参数后
// console.log(getName('zhang'));
// 默认参数 是可以放在必选参数以及可选参数之后
console.log(getName('张三'));
③剩余参数和函数重载
* 剩余参数:rest参数(和es6语法)
* 函数重载:函数名相同,形参不同的多个函数
// 剩余参数
function fn(x: string, y: string, ...args: number[]) {
console.log(x, y, args);
}
fn('', '', 1, 2, 3, 4, 5)
// 函数重载 :函数名相同,形参不同的多个函数
// 数字 相加, 字符串 拼接 ... 联合类型
// 函数重载声明,可以使用重载定义多个 newAdd 的函数类型
function newAdd(x: string, y: string): string
function newAdd(x: number, y: number): number
// 函数实现
function newAdd(x: string | number, y: string | number): string | number {
if (typeof x == 'string' && typeof y == 'string') {
// ...
return x + y//字符串拼接
} else if (typeof x == 'number' && typeof y == 'number') {
// ...
return x + y//数字相加
}
}
// 这样有一个缺点,就是不能够精确的表达,
// 输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串
console.log(newAdd(1, 2));
console.log(newAdd('张', '三'));
④类型断言(可以手动指定一个类型)
2种方式:
1、变量 as 类型
2、<类型>变量
// 将一个联合类型断言为其中一个类型
// Ⅰ 将任何一个类型断言为 any,any类型是访问任何属性和方法的
Ⅱ 它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any
Ⅲ 一方面不能滥用 as any,另一方面也不要完全否定它的作用,我们需要在类型的严格性和 开发的便利性之间掌握平衡
// 将 any 断言为一个具体的类型
// 定义一个函数,获取到一个数字或者字符串的长度
// 类型断言:可以手动指定一个类型
// 2种方式
// 1、变量 as 类型
// 2、<类型>变量
// 将一个联合类型断言为其中一个类型
function getLength(x: string | number): number {
if ((x as string).length) {
return (<string>x).length
} else {
return x.toString().length
}
}
console.log(getLength('123'));
console.log(getLength(123));
// 将任何一个类型断言为 any,any类型是访问任何属性和方法的
// 它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any
// 一方面不能滥用 as any,另一方面也不要完全否定它的作用,我们需要在类型的严格性和开发的便利性之间掌握平衡
(window as any).a = 10
// 将 any 断言为一个具体的类型
function abc(x: any, y: any): any {
return x + y
}
let a = abc(1, 2) as number//a-->数值类型
let b = abc('1', '2') as string //b-->字符串类型
六、进阶
①类型别名
通过type去定义 ,用来给一个类型起个新名字
②字符串字面量类型
通过type去定义 ,用来约束取值只能是某几个字符串中的一个
// 用来约束取值只能是某几个字符串中的一个
// 张三丰,张三,张大炮
type stringType = "张三丰" | "张三" | "张大炮"
let names: stringType = "张三"
③元组Tuple
合并了不同类型的对象
let arr:number[]=[1,2,3,4]
// 数值和字符串
// 元组(Tuple)合并了不同类型的对象
let Tarr:[number,string]=[123,"123"]
// 添加内容的时候,需要是number或者string类型
Tarr.push(456)
Tarr.push("456")
// Tarr.push(true) 报错
④枚举Enum
给一组数值赋予名称
枚举类型ts代码
// 使用枚举类型给一组数值赋予名称
// 可以通过名称去拿取值,通过值去拿取名称
// 枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射
// 1,2,3,4
enum NumberType {
one = 2,//手动赋值,没有赋值,第一个参数默认为0,后面递增加一
two = 1,//后面的值如果没有手动赋值,会根据前面的值进行递增加一
three,
four
}
// 手动赋值注意:尽量不要写一些重复的值
console.log(NumberType);
// 枚举项有两种类型:常数项(constant member)和计算所得项(computed member)
// 计算所得项需要放置在已经确定赋值的枚举项之前,后面不能存放未手动赋值的枚举项
enum Color {
red,
blue = "blue".length,
green = 11
}
// 常数枚举是使用 const enum 定义的枚举类型
// 常数枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员
const enum Obj {
o,
b,
j = 10 + 10
}
console.log(Obj.o);
console.log(Obj.b);
console.log(Obj.j);
// 外部枚举(Ambient Enums)是使用 declare enum 定义的枚举类型
// declare 定义的类型只会用于编译时的检查,编译结果中会被删除
// 声明文件
declare enum ABC {
a, b, c
}
console.log(ABC.a);
js代码
// 使用枚举类型给一组数值赋予名称
// 可以通过名称去拿取值,通过值去拿取名称
// 枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射
// 1,2,3,4
var NumberType;
(function (NumberType) {
NumberType[NumberType["one"] = 2] = "one";
NumberType[NumberType["two"] = 1] = "two";
NumberType[NumberType["three"] = 2] = "three";
NumberType[NumberType["four"] = 3] = "four";
})(NumberType || (NumberType = {}));
// 手动赋值注意:尽量不要写一些重复的值
console.log(NumberType);
// 枚举项有两种类型:常数项(constant member)和计算所得项(computed member)
// 计算所得项需要放置在已经确定赋值的枚举项之前,后面不能存放未手动赋值的枚举项
var Color;
(function (Color) {
Color[Color["red"] = 0] = "red";
Color[Color["blue"] = "blue".length] = "blue";
Color[Color["green"] = 11] = "green";
})(Color || (Color = {}));
console.log(0 /* Obj.o */);
console.log(1 /* Obj.b */);
console.log(20 /* Obj.j */);
console.log(/* ABC.a */);
七、类
理解:描述了所创建的对象共同的属性和方法
①属性和方法
// 类:描述了所创建的对象共同的属性和方法
// 实例化对象
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHi(str: string) {
console.log('hi,' + str);
}
}
let p = new Person("张三", 18)//new的时候,会执行类中构造方法constructor
p.sayHi('李四')
②静态成员(state)
只属于类自己的属性和方法(静态属性、静态方法)
// 静态方法
// 静态属性
// 只属于类自己的属性和方法
class A{
static name1:string
// constructor(name:string){
// this.name=name
// }
static sayHi(){
console.log('hi');
}
}
const a1=new A()
console.log(A.name1);
A.sayHi()
// console.log(a1.name);报错
// a1.sayHi()报错
③继承
*调用父类的构造函数,使用的super
*可以调用父类的方法,还可以重写这个方法
// 总结:类与类之间存在继承关系,通过extends进行继承
// 子类可以调用父类的方法,通过super
// 子类还可以重写父类的方法
// 扩展现有的类,通过继承
// 继承:类与类之间的关系
// 子类继承父类
class Animal {//父类
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHi(str: string) {
console.log('hi,' + str);
}
}
// 子类
class Dog extends Animal {
constructor(name: string, age: number) {
// 调用父类的构造函数,使用的super
super(name, age)
}
// 可以调用父类的方法,还可以重写父类的方法
sayHi() {
console.log('我是dog类的sayhi方法');
super.sayHi('狗狗')
}
}
const a=new Animal('小猫',3)
a.sayHi('小猫')
const d=new Dog('拉布拉多犬',2)
d.sayHi()
// 总结:类与类之间存在继承关系,通过extends进行继承
// 子类可以调用父类的方法,通过super
// 子类还可以重写父类的方法
④存取器:可以帮助我们控制对对象成员的访问
get 属性(){}
set 属性(){}
// 存取器,可以帮助我们控制对对象成员的访问
class Name {
firstName: string
lastName: string
constructor(firstName: string, lastName: string) {
this.firstName = firstName
this.lastName = lastName
}
// 设置存取器
// 读取器-->用来读取数据
get fullName(){
// 姓名=姓氏+名字
return this.firstName+"-"+this.lastName
// return "张三"
}
// 设置器-->用来设置数据
set fullName(value){
console.log(value);
let names=value.split('-')
this.firstName=names[0]
this.lastName=names[1]
}
}
const n=new Name('张',"大炮")
console.log(n);
n.fullName="张-三丰"
console.log(n.fullName);
⑤修饰符
①public 修饰的属性或方法是公有的(默认)
②private 修饰的属性或方法是私有的
③protected 修饰的属性或方法是受保护的
// public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
// private 修饰的属性或方法是私有的,不能在声明它的类的外部访问,包括其子类也不能,但是这个属性和方法是可以被继承的
// protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
class B{
public gender:string//公有的属性
private name1:string//私有的属性
protected age:number//受保护的属性
public constructor(name:string,age:number){//公有的方法
this.name1=name
this.age=age
}
public p(){
console.log(this.name1);
console.log(this.age);
}
}
class C extends B{
constructor(name1:string,age:number){
super(name1,age)
}
play(){
// console.log(this.name1);报错
console.log(this.age);
}
}
const b=new B("张三",12)
const c=new C('张三丰',13)
// console.log(c.age);
// console.log(b.name1);
// b.p()
c.play()
⑥readonly修饰符
readonly age:number//只读属性,但是在构造函数是可以修改的
readonly以及三个修饰符定义在参数上,那就是创建并且初始化的age参数
class X {
// readonly age:number//只读属性,但是在构造函数是可以修改的
// readonly以及三个修饰符定义在参数上,那就是创建并且初始化的age参数
// constructor(readonly age:number){
// constructor(public age: number) {
// constructor(private age: number) {
constructor(protected age: number) {
// this.age=age
}
update() {
// this.age=15 报错,不能被修改 ,只读属性
}
}
const x = new X(13)
console.log(x);
// x.age=15
⑦抽象类 abstract
*为子类服务
abstract class Y {//定义一个抽象类,为子类服务
abstract name: string//抽象属性
// constructor(name){
// this.name=name
// }
abstract sayHi()//不能有具体实现
}
// 不能够实例化
class Z extends Y {
name: string
constructor(name) {
super()
this.name=name
}
//在子类中去具体实现抽象类中抽象方法
sayHi(){
console.log('hi');
}
}
const z = new Z("张三")
console.log(z.name);
z.sayHi()
⑧类的类型(和接口差不多,对类的实例对象的约束)
class Car {
name: string
constructor(name: string) {
this.name = name
}
}
class Ben extends Car {
age:number
constructor(name) {
super(name)
}
}
// const car: Ben = new Car('')
const ben: Car = new Ben("")
⑨类实现接口 ( implements )
interface ISing {
// 这个方法是没有任何的实现
sing()
}
interface IDance {
dance()
}
class P implements ISing, IDance {//人 唱歌,跳舞
sing() {
console.log('唱歌');
}
dance() {
console.log('跳舞');
}
}
class An implements ISing, IDance {//动物 唱歌,跳舞
sing() {
console.log('唱歌');
}
dance() {
console.log('跳舞');
}
}
const p1 = new P()
const an = new An()
p1.sing()
an.sing()
p1.dance()
an.dance()
**接口继承接口 :接口可以继承其他的多个接口的
interface IRun {
run()
}
interface ISwim {
swim()
}
// 接口可以继承其他的多个接口的
interface IActive extends IRun, ISwim {
}
// ...
class I implements IActive {
run() {
}
swim() {
}
}
10、接口继承类:接口继承类中的实例属性和实例方法
class NewPerson{
name:string
constructor(name:string){
this.name=name
}
sayHi(){
console.log('hi');
}
}
interface IPerson extends NewPerson{//接口继承类中的实例属性和实例方法
age:number
}
let person:IPerson={
name:"",
age:18,
sayHi(){
}
}
11、声明合并
// 函数合并-->函数重载
// 接口合并:合并的属性的类型必须是唯一的
// 类的合并
// 函数合并--》函数重载
// 接口合并
// 合并的属性的类型必须是唯一的
// 类的合并是一样的和接口的
interface Cat {
name: "小橘",
gender:"女"
}
interface Cat {
name: "小橘",
age: 3
}
const cat: Cat = { name: "小橘", age: 3,gender:"女" }
八、泛型
①简单使用(写一个变量T,使用时再明确T的类型)
// 需求:定义一个函数,传入两个参数,第一个参数是数据,第二个参数是数量,
// 函数的作用:根据数量产生对应个数的数据,存放在一个数组中
// (123,3)-->[123,123,123]
// 数值
// T 表示任意输入的类型
function getArr<T>(value: T, count: number): T[] {
const arr: T[] = []
for (let i = 0; i < count; i++) {
arr.push(value)
}
return arr
}
// 原则上不推荐使用any
// 使用泛型,在定义时不需要先确定类型,而是使用的时候再去确定
// 如果没有确定的话。就会走类型推断
console.log(getArr(123,3));
console.log(getArr<string>("123",3)); //这里<string>可写可不写,会走类型推断
// [123,"",true,{},[]]
②多个泛型参数的函数(无疑多写一个泛型变量)
function updateArr<T, U>(t: [T, U]): [U, T] {
return [t[1], t[0]]
}
console.log(updateArr<string, number>(['123', 123]));
console.log(updateArr<boolean, number>([true, 123]));
③泛型约束(可以通过接口实现)
// 获取一个参数的长度
// 泛型约束,约束这个任意输入的类型,必须要有length属性
interface ILength{
length:number
}
function getLength<T extends ILength>(x:T):number{
return x.length
}
console.log(getLength("123"));
④泛型接口
// 定义一个泛型接口(第一种写法)
// interface IArr {
// <T>(value: T, count: number): Array<T> //这是函数接口
// }
interface IArr<T>{ //第二种写法
(value: T, count: number): Array<T>
}
let getArr1: IArr<string> = function <T>(value: T, count: number): T[] { //这里的IArr<string> 对应第二种写法
const arr: T[] = []
for (let i = 0; i < count; i++) {
arr.push(value)
}
return arr
}
console.log(getArr('123',3));
// 简单例子
interface IPerson<T>{
name:T
}
let p:IPerson<string>={
name:""
}
let p1:IPerson<number>={
name:123
}
⑤泛型类(用法差不多)
class Person<T>{
name: string
age: T
constructor(name: string, age: T) {
this.name = name
this.age = age
}
}
const person = new Person<string>("123","18")
const person1 = new Person<number>("123",18)