TypeScript基础
  # 1.前言
什么是typescript?
TypeScript简称TS TS和JS之间的关系其实就是Less/Sass和CSS之间的关系 就像Less/Sass是对CSS进行扩展一样, TS也是对JS进行扩展 就像Less/Sass最终会转换成CSS一样, 我们编写好的TS代码最终也会换成JS TypeScript是JavaScript的超集,因为它扩展了JavaScript,有JavaScript没有的东西。 硬要以父子类关系来说的话,TypeScript是JavaScript子类,继承的基础上去扩展。
为什么需要TypeScript?
简单来说就是因为JavaScript是弱类型, 很多错误只有在运行时才会被发现 而TypeScript提供了一套静态检测机制, 可以帮助我们在编译时就发现错误
TypeScript特点
支持最新的JavaScript新特特性 支持代码静态检查 支持诸如C,C++,Java,Go等后端语言中的特性 (枚举、泛型、类型转换、命名空间、声明文件、类、接口等)
# 2.搭建TypeScript 本地环境
- 全局安装typeScript
 
npm i -g typescript
 - 安装ts-node
 
npm i -g ts-node
 - 初始化一个项目
 
tsc --init
 - 运行ts文件的编译命令[可以通过package.json 配置编译命令]
 
tsc index.ts
 # 3 基本数据类型定义
# 3.1 常用基本数据类型
// 基本数据类型
let str:string="啦啦啦啦";
let num:number = 123;
let bool:boolean=true;
let nu:null = null;
let un:undefined = undefined;
console.log(str,num,bool,nu,un);    //啦啦啦啦 123 true null undefined
 2
3
4
5
6
7
8
# 3.2 ES6 新增基本数据类型
注意: 如果要使用此种数据类型需要将
tsconfig.json中的lib选项解注释并加入以下内容lib: ["ESNext"]或 直接更改ts整体编译 target: ES2020"target": "es2020"
# 3.2.1 长整数 BigInt
const big: BigInt = BigInt(Number.MAX_SAFE_INTEGER);
console.log(big);  //9007199254740991n
 2
# 3.2.2 唯一值 Symbol
let id: symbol = Symbol(1);
let id1: symbol = Symbol(2);
console.log(id);    //Symbol(1)
console.log(id1);  //Symbol(2)
 2
3
4
5
# 3 数组定义
let arr:string[]=['1','2','3','4'];
 let arr1:Array<number> = [100,200,300];
 let arr2:(string|number)[] = ['1',2,'3',4];
 # 4 接口 定义 对象的类型 interface
# 4.1 基础使用
interface User{
    name:string,
    age:number,
}
let user1:User={name:"张麻子",age:40};
 2
3
4
5
# 4.2 可以合并的 类型
interface User{
    name:string,
    age:number
}
interface User {
    like:string,
}
let user:User = {name:'张麻子',age:22,like:'xxxxx'};
 2
3
4
5
6
7
8
9
# 5 对象数组 定义
let list:(User)[] = [{name:"黄四郎",age:45},{name:"马邦德",age:44}]
let list:Array<User> = [{name:"黄四郎",age:45},{name:"马邦德",age:44}]
 2
# 6.元组
和数组类似,但是其中的类型和顺序以及数量都需要被提前定义。
let arr:[string,number,string]= ['张麻子',22,'敲代码'];
//不可以通过索引新增元祖的元素
// arr[3]='啦啦啦';
// 可以通过push追加,这是ts 元祖的缺陷
arr.push('啦啦啦')
 2
3
4
5
//长度可变元祖定义
let arr:[number,...any] = [100]
arr[1] = '你好啊'
 2
3
# 6 函数定义
function sum(a:number,b:number):number{
    return a+b;
}
console.log(sum(100,200));
 2
3
4
// 可选参数类型
let func: (name: string, age?: number) => void;
// 可选参数一定要在必选参数的后面。
func = function (name: string, age?: number): void {
  console.log(name, age);
};
func('zhangsan'); // zhangsan undefined
// 默认参数一定要在必选参数的后面。
func = function (name: string, age: number = 24): void {
  console.log(name, age);
};
func('zhangsan'); // zhangsan 24
 2
3
4
5
6
7
8
9
10
11
12
13
14
# 7 void 空类型
//函数没有返回值
const avg = (a:number,b:number,c:number):void=>{
    console.log(a+b+c);
}
avg(100,200,300)
 2
3
4
5
# 8 any 类型
定义任何变量 可以赋值为任何数据类型,写any 相当于没有类型限制
let num1:any=123;
num1='100';
console.log(num1);   
 2
3
# 9 类的定义 class
# 9.1 简单类的定义
class Person {
    // public 表示实例公共属性
    public name: string;
    public age: number;
    // public可以省略
    like: string;
    constructor(name: string, age: number, like: string) {
        this.name = name;
        this.age = age;
        this.like = like
    }
    // 实例方法
    public say(): string {
        return '你好啊,我是' + this.name;
    }
    // 静态方法
    static eat(food: string): string {
        return '我正在吃' + food;
    }
}
let p1 = new Person('张三', 22, '抽烟,喝酒,烫头');
console.log(p1);
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 9.2 简单类省略写法
每次定义类 都需要在构造函数前面去定义每个属性的类型太过繁琐,可以直接简写到构造函数中
- 原写法
 
class Person {
    // public 表示实例公共属性
    public name: string;
    public age: number;
    // public可以省略
    like: string;
    constructor(name: string, age: number, like: string) {
        this.name = name;
        this.age = age;
        this.like = like
    }
    ...
}
 2
3
4
5
6
7
8
9
10
11
12
13
- 简写 直接把定义属性的关键字放到构造函数中
 
class Person {
    // 省略写法,不需要提前定义类型
    // public name: string;
    // public age: number;
    // like: string;
    constructor( public name: string,  public age: number, public like: string) {
        // 省略写法,不需要再去对每个属性赋值
        // this.name = name;
        // this.age = age;
        // this.like = like
    }
    ...
}
 2
3
4
5
6
7
8
9
10
11
12
13
# 9.3 必传与可选参数
?: 可选参数,可以不传,默认类型为隐式any,默认值为undefined
!: 必传参数,必须传递
class Person {
    // !:  必传参数
    public name!: string;
    // ?:  可选参数,默认类型 number | undefined ,默认值:undefined 
    public age?: number;
    constructor(name: string, age?: number) {
        this.name = name;
        this.age = age;
    }
}
let p1 = new Person('张三', 22);  //Person { name: '张三', age: 22 }
let p2 = new Person('李四')       //Person { name: '李四', age: undefined }
// let p3 = new Person();  //报错 必须传递1-2个参数
 2
3
4
5
6
7
8
9
10
11
12
13
14
# 9.4 类的修饰符
类的属性和方法 有特定的作用或者限制 就需要通过修饰符进行处理
js中类的修饰符: public static readonly(只读)
ts中类的修饰符:除了js类的三个修饰符之外,新增了:
- public (公共) 公共属性、方法,可以在实例以及子类中访问
 - protected (保护) 只能在子类中访问该属性和方法
 - private(私有) 只能在当前类中访问该属性和方法
 
class Person {
    
    protected age!: number;
    private name!: string;
    public sex!: string;
  
    private getName() {}
    protected getAge() {}
    public getSex() {}
  
    // name 和 getName() 只能在 Person 中用 this 访问
  }
  class User extends Person {
    constructor() {
      super();
      // User中可以访问 getAge() 和 age 以及被 public 修饰的属性以及方法
    }
  }
  const person = new Person(); // person 只能访问到 getSex() 和 sex
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意: private 私有属性与方法 只能在当前类中使用,不能通过子类 和 this实例访问
class Person{
    private name:string;
    constructor(name:string){
        this.name = name;
    }
    private say(){
        return 'ssss';
    }
    // 私有方法只能通过当前类进行使用
    public xxx(){
        console.log(this.name);
        console.log(this.say())
    }
}
let p1 = new Person('张麻子');
// p1.name = '黄四郎'  // 私有属性 使用报错
// p1.say()   //私有方法调用报错
p1.xxx()     //通过实例正确的使用方法
console.log(p1);    //Person { name: '张麻子' }
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 9.5 类的存取器
如果想通过 实例对象直接访问或修改 类中的私有属性与方法,除了通过公有方法进行调用与操作之外,TS提供了存取器来进行操作
TS 支持通过
getter/setter来截取对象成员的访问假设我们现在有个类,其中
name是私有属性,但是我们又想要更改以及访问这个属性,此时就可以使用存取器Object.defineProperty 也拥有存取器
class Person{
    private _name:string;
    constructor(name:string){
        this._name = name;
    }
    // 获取name时自动执行
    get name():string{
        return this._name;
    }
    // 修改name时 自动修改_name 
    set name(val:string){
        this._name = val;
    }
}
let p1 = new Person('张麻子');
console.log(p1.name)    //自动执行 get     张麻子
p1.name = '黄四郎'      //自动执行 set   
console.log(p1.name)   //黄四郎
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 10.枚举类型 enum
作用:一般用来定义 多种不同的状态
enum State{
    Pending="PENDING",
    Success="SUCCESS",
    Fial="FIAL"
}
console.log(State.Pending)    //PENDING 
 2
3
4
5
6
// 无默认值的枚举值 默认从 0 开始
enum State{
    Pending,
    Fial,
    Success,
}
console.log(State.Pending)   //0
console.log(State.Fial)     //1
console.log(State.Success)  //2
 2
3
4
5
6
7
8
9
# 11.泛型
泛型: 泛指一切类型
泛型可以理解为 类型变量: 可变的类型
# 11.1 场景:
如果一个函数 传入了数字类型,就返回数字 ,如果传入了字符串,就返回一个字符串
function fn(a:number):number{
    return a;
}
function fn(a:string):string{
    return a;
}
 2
3
4
5
6
- 泛型写法
 
function fn<T>(a:T):T{
    return a;
}
fn(100)  //100
fn('张麻子')  //张麻子
fn(true)  //true
//调用时传入泛型的类型
fn<number>(1000)  //1000
fn<object>({name:'张麻子'}) //{name:'张麻子'}
 2
3
4
5
6
7
8
9
# 11.2 理解:

# 11.3 泛型语法说明
为什么是 T ? 难道不能是其他的字母或者单词吗?当然可以 可以使任意单词或者字母,只要语义化清晰即可。建议首字母大写。当然 T 并不是毫无道理。
- T : 代表 Type 在定义泛型时通常用作第一个类型变量名称.
 - K : 表示对象中的 key 类型。
 - V : 表示对象中的 value 类型。
 - E : 表示 Element 元素类型。
 
# 11.4 多个泛型参数
function fn<N,M>(a:N,b:M):N{
    return a;
}
console.log( fn(1000,'sss')   );   //1000
 2
3
4
# 11.5 泛型 约束
使用泛型之后 会造成一些问题: 泛型之间不能使用运算符,泛型拿到数组、元祖之后没有length
解决length问题需要使用到 泛型约束
泛型约束:可以理解为 该泛型可以包含的属性及属性类型
这里出现
extends关键字,此处表示泛型必须包含某种类型,从而在调用时约束了传入的类型。
function fn<T>(arr:T):number{
    return arr.length;    //报错  泛型T 不存在 length属性
}
console.log( fn([100,200,300])  );
 2
3
4
5
- 泛型约束写法
 
// 定义泛型的约束
interface Len{
    length:number
}
function fn<T extends Len>(arr:T):number{
    return arr.length;    
}
console.log( fn([100,200,300])  );   //3
 2
3
4
5
6
7
8
- 多个泛型约束
 
如果需要约束多个条件
<T extends Type1, Type2, ...>
# 11.6 泛型问题
泛型之间不能使用运算符
解决方案: 函数重载
function add<T>(a:T,b:T):T{
    return a+b  //报错 两个泛型不能进行加法运算
}
 2
3
- 函数重载
 
需要注意的是只有最后一个才是真正的函数,其他的地方只能是函数类型的定义。
function add(a: string, b: string): string
function add(a: number, b: number): number
function add(a: any, b: any) {
    return a + b;
}
console.log(add(11,22));    //33
console.log(add('ssss','eeeee'));  //sssseeeee
 2
3
4
5
6
7
# 12 never 不存在的类型
抛出异常,或者永远不会有返回值的函数以及永远不会为真的类型返回
never。
function infiniteLoop(): never {
    while (true) {}
  }
  function error(): never {
    throw '异常错误';
  }
  error()
  infiniteLoop()
 2
3
4
5
6
7
8
9
# 13 unknown 未知类型
unknown 和 any 很类似
定义为 unknown 类型的变量可以接收 任意数据类型
unknown 的变量只能赋值给 unknown 和any 类型的变量
any 定义的类型变量可以赋值给任意类型变量
let a:unknown;
a=1;
a='222'
console.log(a);  //'222'
 2
3
4
let a:unknown = '100';
// let b:string = a;  //不能讲unknown 类型 分配给string类型
let c:unknown = a;    //'100'  
let d:any = a;       //'100'
 2
3
4
# 14 as 类型断言
在某种情况下我们可能明确的知道这个值的类型,但是静态检查不是想要的类型。此时可以使用类型断言确定这个值的类型
let arr = [111,'2222',333];
// let len = arr[1].length;   //报错 很明显'2222'是一个字符串,但是却没有length属性
// 使用强制断言 可以强制确定该变量的类型
let len = (arr[1] as string).length;  //4
 2
3
4
5
6
# 15 联合类型
表示一个类型可以是多种类型中的一种
// 两个类型 选择一个
let a: string | number;
a = 1;
a = '1';
console.log(a)  //'1'
 2
3
4
5
# 16 type 类型别名
type 可以对 比较复杂的类型 取一个别名,简化 ts 的代码
type 和 interface 有很多相同之处 也有不同之处
相同:type 和interface 都可以定义 对象和 函数的类型
不同:
interface 是接口:用于描述一个对象 使用 extends 或implements 是 必须使用 interface
type 是类型别名: 使用组合或者交叉类型 必须使用type
# 16.1 type 定义类型别名
type Name = string    //type 取别名
type NameResolver = () => string   //type 取别名
type NameOrResolver = Name | NameResolver          // 联合类型 通过type 取别名 简化代码
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n
    }
    else {
        return n()
    }
}
getName('黄四郎');  //'黄四郎'
getName(()=>'张麻子')  //'张麻子'
 2
3
4
5
6
7
8
9
10
11
12
13
14
# 16.2 type 定义对象类型 与interface 相同
interface User{
    name:string,
    age:number
}
let user:User = {name:'张麻子',age:30};
 2
3
4
5
type User = {
    name:string,
    age:number
}
let user:User = {name:'张麻子',age:22};
 2
3
4
5
# 16.3 interface 可以合并多个类型,type 不支持
interface User{
    name:string,
    age:number
}
interface User {
    like:string,
}
let user:User = {name:'张麻子',age:22,like:'xxxxx'};
 2
3
4
5
6
7
8
9
type User = {
    name:string,
    age:number
}
type User = {   //报错 User 标识符重复
    like:string,
}
 2
3
4
5
6
7
8