TypeScript学习笔记
TypeScript概述和环境
TypeScript(简称:TS)是JavaScript的超集(JS有的TS都有)。
TypeScript = Type + JavaScript(为JS添加了类型系统)
安装解析TS的工具包typescript

安装步骤:
-  打开VSCode终端。 
-  输入安装命令:npm i -g typescript 敲回车,来安装(注意:需要联网)。 

- 安装查看: tsc -v / tsc --version
安装简化执行TS的工具包 ts-node
- 这个工具包内部会先用tsc命令, 把TS文件转为JS文件
- 内部再使用命令ts-node,用来执行生成的JS代码。
- 后期在脚手架中使用ts就不会这么麻烦, 初学语法暂时使用这个方式
入门代码
创建 hello.ts 文件,console.log(“hello TS”)
在终端进行执行:tsc hello.ts
会生成一个 hello.js 文件

TS中的注释
// 单行
/* 多行 */
JS原有类型
number
数值类型- number(整数和浮点数)
除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量。
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
string
-  string 单引号(推荐)或双引号或``都可以 let name: string = “bob”; 
 name = “smith”;
boolean
-  布尔值 boolean true/false值 let isDone: boolean = false; 
undefined
-  表示此处应该有值, 但是现在没有值 
-  申明了一个变量, 但是未赋值, 这个变量就是undefined 
-  在TS中, 可以给已有类型的基本数据类型变量, 重新赋值undefined let u:undefined = undefined 
null
-  表示此处不应该有值, 没有值了 
-  在TS中, 可以给已有类型的引用数据类型变量, 重新赋值null let n: null = null 
object
对象类型基本使用
-  js中属于对象的太多,添加object后等于是没有添加类型限制,开发很少用 
-  示例1: // {}也表示对象,表示变量o必须是一个对象类型,在其中可以指定属性类型 // 以下示例表示对象o中只能有指定的属性名称和类型,以及个数 // 在属性名后面加?,表示该属性可选 let o: { name: string, age: number, sex?: string }; o = {name: '孙悟空',age: 18} // ok o = {name: '孙悟空'} // 报错,少了一个age属性 o = {name: '孙悟空',age: 18,sex: '男'} // ok示例2: // 需求:在一个对象中,我只有一个属性是有要求的,其他的属性名称、类型、个数都未知 let o: { name: string, [propertyName: string]: any } /* * 以上代码,[propertyName: string] 表示属性名是string(对象的属性名都是字符串类型) * [propertyName: string]: any 表示该对象中的属性是任意类型, * 如果any是string,表示该对象中的属性都必须是string类型 */实例3: function getObj(obj: object):object { // do something return { name:"卡卡西" } } console.log(getObj({name:"佐助"})) console.log(getObj(new String('字符串')))
对象类型中的函数写法
-  写法一: sayHi(name:string):voidconst user: { name:string sayHi(name:string):void } user = { name:'张三' sayHi(name) { console.log('你好,我叫' + name) } }
-  写法二: add(n1:number, n2:number) => numberconst user: { name:string add(n1:number, n2:number) => number } user = { name:'李四' add: function(a, b){ return a+b } }
array
-  数组; 开发中, 数组中的元素为同一类型 
-  语法1: 类型[]// 表示数值数组 let list: number[]; let list: Array<number>; let list: number[] = [1, 2, 3];
-  语法2:使用泛型写法, Array<元素类型>:// 表示字符串数组 let list: string[]; let list: Array<string> let list: Array<number> = ['a', 'b', 'c'];
function
函数类型的定义
- 函数主要是限制出入参的类型及个数, 并无固定的针对性语法, 实现方式多种
-  单独限制出入参类型 -  箭头函数的写法常用 // 箭头函数 
 let addFunc = (num1:number, num2:number):number => {
 return num1 + num2
 }addFunc = function(n1, n2): number{ 
 return n1 + n2;
 }
 console.log(addFunc(2, 6)) // 8
 // 函数声明 
 function add(num1:number, num2:number):number {
 return num1 + num2
 }
 console.log(add(2,2)) // 4
 
-  
-  使用类型别名 type AddFun = (num1:number, num2:number):number const add:AddFun = (num1, num2) => { return num1 + num2 }-  注意: 只适合函数表达式 // 函数表达式 let fun1 = function(){} // 函数声明 function fun2(){}
 
-  
-  使用接口 -  为了使用接口表示函数类型,需要给接口定义一个调用签名 interface ISearchFunc { 
 // 定义调用签名
 (source:string, subString:string): boolean
 }// 定义一个函数,该类型即为上面的接口 
 const searchString: ISearchFunc = function (source:string, subString:string): boolean {
 return source.search(subString) > -1
 }// 调用 
 console.log(searchString(‘道阻且长,行则将至’,‘长’)) // true
 
-  
扩展 - 函数的参数
默认值
function(name: string, age: number = 18){
}
可选参数
-  添加 ?,接口也适用(表示属性可省略)
-  在入参变量名后面添加 ?, 注意可选参数必须放在最后, 可以是一个或多个可选参数
-  函数体中对可选参数需使用非空校验相关逻辑 // 表示入参age, sex,可省略 
 function(name: string, age?: number, sex?: string){} 
TS新增类型
any
-  任意类型(any可略) , 尽量不要使用any 
-  设置为该类型后等于对变量关闭了TS的类型检测, 直接让它们通过编译阶段的检查 
-  可以赋值给任意类型变量 
-  // eg1:多次赋值均不报错 let a: any = 123 a = "str"a = true // eg1:它可以赋值给其他任意类型,使其他类型也称为any类型 // 简单理解就是b是any类型,经过下面赋值,使a也成了any类型 let b:number = 123 b = a
unknown
-  未知类型的值; 编码中,尽量使用unknown代替any 
-  和any的异同点 - 同:都是可以多次赋值不同类型的值
- 异:any可以赋值给任意任意类型的值,使其也称为any的值,但是unknown不能直接赋值其他类型的值
 
-  一般来说,这个类型并不是开发者手写的,是网络传来的,需要配和断言使用(在使用的时候需要明确这个变量的类型,可以多次指定类型) type A = {name:string} type B = {age:number} // 模拟ajax传递过来的数据 let c: unknown = JSON.parse("{'name':"Tom"}") let var1 = (c as A).name let var2 = (c as B).age
void
-  空值 , 用于函数返回值 表示函数没有返回值(常用) 
-  实际编码中,其实可以return null或者return undefined,但是没有意义,应该是语法上的兼容而已 function fn(): void{ 
 // do something
 return; // 或者不写return
 }
never
- 永不存在的值的类型
- 开发中使用较少,一般用于抛出异常、无限循环的函数返回类型
- 出现该类型的时候,注意检查代码是否有问题
语法:
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {    
	throw new Error(message);
}
// 推断的返回值类型为never
function fail() {    
	return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {    
	while (true) {
	}
}
eg:
type Code = 1|2|3|undefined
let dir:Code // 表示dir的取值只能是”1,2,3,undefined“ 四者之一
switch(dir){    
	case 1:        
		break;    
	case 2:        
		break;    
	case 3:        
		break;    
	case undefined:      
		break;    
	default:        
		console.log('如果进入该分支,表示dir的值不在”1,2,3,undefined“中, 即为never类型')}
元组
-  固定长度的数组(元素类型可以不一致) 
-  结合数组理解, 最大的区别的是元组的长度固定, 且每个索引对应的元素类型固定 
-  对于值, 可以get, set, update, 注意变更的值只能是规定的类型, update可以用arr[0] = newValue, set可以用arr.push(newValue) // 表示数组中只能有2个string的元素,长度不可变 let arr1: [string, string] let arr2: [number, string]
enum
- 枚举; 默认枚举值有编号,类似数组索引,从0开始
eg1:
enum Gender {     
	Male,     
	Female
}
let tom: Gender = Gender.Male
console.log(tom)  // 0
console.log(Gender.Male, Gender.Female) // 0 1
- 特殊使用方式:
如果枚举值是数字值, 可以用以下写法
enum Color {  
	Red = 6,  
	Green = 7,  
	Blue = 8
}
let c:string = Color[6]  // Red
联合类型
-  把多个类型联合为一个类型 (表示取值可以为多种类型中的一种) 
-  用 |隔开// 表示入参param可以是number或者string类型 
 // 出参为string类型
 function getSomeThing(param: number|string):string {
 return param+’’
 }getSomeThing(123) 
 getSomeThing(“字符串”)type MyType = 1 | 2 | 3 | 4 | 5; let a: MyType = 2; 
 let b: MyType = 4;// 数组中,既可以有字符串,也可以有数字 
 let arr:(number|string)[]arr = [123, ‘哈哈’]a 
 rr = [66, 88, 99]
typeof 操作符
interface Person {  
	name: string;  
	age: number;
}
const test: Person = { name: "Tom", age: 20 };
type Sys = typeof test; // type Sys = Person
在上面代码中,我们通过 typeof 操作符获取 test 变量的类型并赋值给 Sys 类型变量,之后我们就可以使用 Sem 类型:
const Vivo: Sys  = { name: "Lili", age: 3 }
也可以对嵌套对象执行相同的操作:
const kakaxi = {    
	name: "kakaxi",    
	age: 27,    
	address: {      
		province: '湖南',      
		city: '长沙'       
	}
}
type Kakaxi = typeof kakaxi;
/*	以下即为Kakaxi类型 
	type Kakaxi = {    
		name: string;    
		age: number;    
		address: {        
			province: string;        
			city: string;    
		};
	}
*/
此外, typeof 操作符除了可以获取对象的结构类型之外,它也可以用来获取函数对象的类型,比如
function toArray(x: number): Array<number> {  
	return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]
类型断言
断言语法
- 同java中的类型转换
- 关于断言是用来跳过编译检查的语法
as语法(推荐):
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
<> 语法:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
断言扩展
非空断言 !
- !表示此处一定有值, 跳过类型检查
如果编译器不能够去除 null 或 undefined,可以使用非空断言 ! 手动去除。
function fixed(name: string | null): string {  
	function postfix(epithet: string) {    
		return name!.charAt(0) + '.  the ' + epithet; // name 被断言为非空  
	}  
	name = name || "Bob"  
	return postfix("great")
}
代码解释:
第 2 行,postfix() 是一个嵌套函数,因为编译器无法去除嵌套函数的 null (除非是立即调用的函数表达式),所以 TypeScript 推断第 3 行的 name 可能为空。
第 5 行,而 name = name || "Bob" 这行代码已经明确了 name 不为空,所以可以直接给 name 断言为非空(第 3 行)。
双重断言
双重断言极少有应用场景,只需要知道有这种操作即可:
interface User {  
	nickname: string,  
	admin: boolean,  
	group: number[]
}
const user = 'Evan' as any as User
代码解释: 最后一行,使用 as 关键字进行了两次断言,最终变量 user 被强制转化为 User 类型。
扩展 - 类型的其他用法
类型别名
-  为任意类型取别名, 语法: type 别名 = 类型
-  使用场景: 当同一类型(复杂)被多次使用时, 可以通过类型别名, 简化对该类型的编码 
-  需要使用关键字 type, 别名命名, 建议首字母大写type CustomArray = (number|string)[] let arr1:CustomArray = [1, ‘啊’, 6] 
 let arr2:CustomArray = [‘x’, ‘y’, 8]
一个变量指定多个类型
// 表示a可以是string或number类型
let a: string | number;
扩展&使用
let j:{name: string} & {age: number}
j = {	
	name: '卡卡西',  
	age: 25
}
j = {name: '鸣人'} // 报错,删了一个age属性
继承、实现概述
extends
- 调用父类使用super
- 子类继承父类的属性和方法
- 子类可以改写父类的属性和方法
类的继承
class A {    
	name:string;    
	constructor(name:string){        
		this.name = name    
	}
}
class B extends A {    
	age:number;    
	constructor(age:number, name:string){        
		// 必须先用super调用父类构造器        
		super(name)        
		this.age = age    
	}
}
class C extends B {     
	sex:string;    
	constructor(sex:string, age:number, name:string){        
		super(age,name)        
		this.sex = sex    
	}
}
const instance = new C('张三', 18, '男')
console.log(instance)
// C { name: '男', age: 18, sex: '张三' }
接口的继承
interface A {    
	id: number
}
interface B {    
	code: string
}
interface C extends A,B {    
	isFlag: boolean
}
const test:C = {    
	id:200,    
	code:'凌凌漆',    
	isFlag:false
}
console.log(test)
// { id: 200, code: '凌凌漆', isFlag: false }
implements
类、接口、抽象类
类
- 类最单纯的理解可以看做是代码的封装, 好比数组是储存数据, 而类可以存储的更多, 还可以存储代码
接口
- 简单理解成一种约束、规则.
- 主要通过定义抽象方法, 让继承的子类去实现
- 试着理解如果引入一个插件, 需要创建一个类, 写固定的方法进行配置, 在插件里就可以通过定义接口, 让用户实现该接口, 进而约束用户进行固定配置的编码
##抽象类
-  开发不常用 
-  里面既可以有具体方法, 也可以有抽象方法 
-  既是对子类, 公共代码的封装; 也是对子类, 需要具体编码的方法的约束 
-  可以结合上述两者的功能, 对其理解 
接口及接口中属性的扩展
接口
-  类比java,可看做一种强制性的规范,一种约束 interface IPerson { 
 firstName:string
 lastName:string
 }function showFullName (person:IPerson) { 
 return${person.firstName}_${person.lastName}
 }let kakaxi:IPerson { 
 firstName:‘旗木’
 lastName:‘卡卡西’
 }console.log(showFullName(kakaxi)) 
 // 旗木_卡卡西
接口继承
-  和类的继承是一个概念 
-  TS支持继承多个接口 interface point2D { x: number y: Number } interface point3D { z: number } interface point4D extends point2D, point3D { w: number } const pointTest: point4D = { x: 100, y: 200, z: 300, w: 400 } console.log(pointTest)
扩展 - 只读和可省略
-  readonly 只读 
-  可省略 interface IAnimals { 
 // id是只读的number类型
 readonly id:number
 name:string
 age:number
 // sex可省略
 sex?:string
 }const dog:IAnimals = { 
 id: 1,
 name: ‘来福’,
 age: 2,
 sex: ‘男’
 }
类、继承、super
类的概念
- 类比Java
- 类的类型,通过接口定义
- 一个类可以同时实现多个接口
- 类里面定义构造函数contructor,是为了实例化的时候对属性值进行初始化
interface IFly {    
	fly:()
}
// 定义一个类,这个类的类型就是上面定义的接口
class Person implements IFly {    
	fly() {        
		console.log("飞上天,和太阳肩并肩")    
	}
}
const p1 = new Person()
p1.fly()
interface IFly {    
	fly:()
}
interface ISwim {    
	swim:()
}
class Person implements IFly,ISwim {    
	fly() {        
		console.log("会飞天")    
	},     
	swim() {        
		console.log("会游泳")    
	}
}
const p2 = new Person()
p2.fly()
p2.swim()
interface TMyFlyAndSwim extends IFly,ISwim {}
class Person implements TMyFlyAndSwim {    
	fly() {        
		console.log("会飞天")    
	},     
	swim() {        
		console.log("会游泳")    
	}
}
const p3 = new Person()
p2.fly()
p2.swim()
继承
-  同比Java 
-  super关键字可以调用父类的构造器,也可以调用父类中的实例函数 class Person { name:string age:number gender:string sayHi(food:string){ console.log(`我是${this.name},喜欢${food}`) } constructor(name:string,age:number,gender:string) { this.name = name this.age = age this.gender = gender } } class Student extends Person { constructor( name:string, age:number, gender:string) { // 使用super,调用父类的构造器实例化 super(name,age,gender) { } // 调用父类中的方法 sayHi(food:string) { super.sayHi(food) } } const p1 = new Person("张三",25,"男") p1.sayHi('西瓜') // 我是张三,喜欢西瓜 const s1 = new Student('小明',18,'男) s1.sayHi('草莓') // 我是小明,喜欢草莓
 
多态
-  父类引用,指向子类对象 
-  不同类型的对象,针对相同的方法,产生了不同的行为 class Animal { 
 name:string
 constructor(name:string){
 this.name = name
 }
 run(distance:number) {
 console.log(${this,name}跑了${distance}米远的距离)
 }
 }class Dog extends Animal { 
 constructor(name:string) {
 super(name)
 }
 // 重写run函数
 run(distance:number) {
 console.log(${this,name}跑了${distance}米远的距离-----dog)
 }
 }class Pig extends Animal { 
 constructor(name:string) {
 super(name)
 }
 // 重写run函数
 run(distance:number) {
 console.log(${this,name}跑了${distance}米远的距离======pig)
 }
 }const ani:Animal = new Animal(‘动物’) 
 ani.run()const dog:Animal = new Dog(‘旺财’) 
 dog.run()const pig:Animal = new Pig(‘佩奇’) 
 pig.run()// 使用多态 
 const dog1:Animal = new Dog(‘小狗’)
 dog1.run()
 const pig1:Animal = new Dog(‘小猪’)
 pig1.run()
修饰符
- public: 默认;表示公共的,被修饰的成员,任何位置都可访问;
- private:表示私有的,被修饰的成员,仅类中可访问;子类的类中也不能访问(this.xxx);
- protected:表示受保护的,被修饰的成员,子类的类中可访问(this.xxx);外部不可访问;
泛型
基本概念
-  函数名称后加 <>, 一般使用符号T占位, T代表具体的类型
-  入参、出参的类型一般具有关联性 function identity(…arg: T[]): T[] { 
 return […arg]
 }
 console.log(identity(1,2,3))
keyof关键字
代码释义:
表示只能是传入OOO类型, OOO类型中的属性之一(“id” | “code” | “age”)
let obj = {    
	id:"100",    
	code:996,    
	age:18
}
type OOO = typeof objfunction main<T, P1 extends keyof T>(obj:T, prop1: P1) {  
	console.log(obj[prop1]);
}
main(obj, 'id');   // '100'
main(obj, 'code'); // 996
main(obj, 'age');  // 18
泛型约束
- 一般通过两种方式实现 
  - 指定更加具体的类型
- 添加约束
 
eg1: 限制出入参为数组, 数组元素类型不固定
function func<T>(param: T[]): T[] {  
	return param
}
func([1,2,3,4])
eg2: 传入的类型, 必须含有 length 属性
interface ILength {    
	length: number
}
function getId<T extends ILength>(param: T): T {    	
	console.log(param.length)    
	return param
}
// 虽然字符串和数组并没有继承 ILength, 但自身拥有length属性
console.log(getId('农夫山泉'));
console.log(getId([11,33,44]));
eg2: 传入的类型是一个对象, 必须拥有name, age属性
interface IUser {  
	name: string  
	age: number
}
function func<T extends IUser>(param: T): T {  	
	console.log(param.name)    
	console.log(param.age)    
	return param
}
泛型在接口中的使用
interface IUser<T> {  
	getId: (param: T) => T
}
泛型工具类
- 泛型工具类型:TS 内置了一些常用的工具类型,来简化 TS 中的一些常见操作
- 说明:它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用。 这些工具类型有很多,主要学习以下几个:
- Partial<Type>
- Readonly<Type>
- Pick<Type, Keys>
Partial
-  Partial 用来构造(创建)一个类型,将 Type 的所有属性设置为可选。 type Props = { 
 id: string
 children: number[]
 }type PartialProps = Partial 
-  解释:构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的。 
Readonly
-  Readonly 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)。 type Props = { 
 id: string
 children: number[]
 }type ReadonlyProps = Readonly 
-  解释:构造出来的新类型 ReadonlyProps 结构和 Props 相同,但所有属性都变为只读的。 let props: ReadonlyProps = { 
 id: ‘1’,
 children: []
 }
 // 错误演示
 props.id = ‘2’
-  当我们想重新给 id 属性赋值时,就会报错:无法分配到 “id” ,因为它是只读属性。 
Pick
-  Pick<Type, Keys> 从 Type 中选择一组属性来构造新类型。 interface Props { 
 id: string
 title: string
 children: number[]
 }
 type PickProps = Pick<Props, ‘id’ | ‘title’>
-  解释: - Pick 工具类型有两个类型变量:1 表示选择谁的属性 2 表示选择哪几个属性。 2. 其中第二个类型变量,如果只选择一个则只传入该属性名即可。
- 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。
- 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。
 










