一天入门TypeScript

394 阅读13分钟

TypeScript 是什么

  1. TypeScript 是微软开发的开源编程语言,2012年10月,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript,可以在任何运行javascript 的地方运行

image.png

TypeScript 简称 : TS ,是Javascript的超集.

简单来说就是,Javascript有的TS都有,js的代码可以TS 环境中运行. TS 在js的基础上添加了类型支持(type),type+script就是TS

  1. TS和js的区别 js属于动态编程语言,TS属于静态编程语言
js是边解析边执行,错误在运行是发现
TS是先解析在执行,在编写代码时就能发现错误(不能直接执行,需要编译成js)

2.1 TS兼容JavaScript,可以解析成JavaScript

image.png

TS初体验

全局安装编译TS的工具包

安装包 : npm i -g typescript

typescript包 : 用来解析TS代码的包,提供了tsc 命令,实现了ts=>js的转化

验证是否安装成功 : tsc -v

编译并运行TS代码

步骤:

2.1 创建ts文件 格式 : xxx.ts (注意: TS 文件的后缀为.ts)

2.2 编译 将TS编译为js 在终端输入命令 tsc xxxx.ts

2.3 执行 js代码 (两种都行)

  2.3.1 在node 中运行, 输入命令 `node  xxx.ts`
  2.3.2 在浏览器中运行

简化运行 TS 的步骤

原因 : 每次修改代码都需要执行两次命令,太繁琐

简化方式 :

  使用 `ts-node` 包,直接在node.js中执行TS代码,使用 `ts-node` 命令,可以简化执行命令
  
安装命令 :

安装包 : `npm  i  -g  ts-node`

使用方式 : `ts-node  xxx.ts`

相当于 : ts-node 命令在内部偷偷将TS转换为js,然后再执行js代码

运行TS代码报错的问题

    Cannot find name 'console'. Do you need to change your target library? Try changing the lib compiler option to include 'dom'.`

原因 :没有创建ts项目,没有写配置文件

解决方式 : 用 tsc --init 命名,在根目录下生成配置文件tsconfig.json

如果使用了 bigint 类型 会报错:

image.png

主要原因:bigint是es 2020新增语法,需要在项目根目录下添加tsconfig.json文件添加es配置项

解决方式 :

image.png

同名变量解决冲突问题

原因 : 目前写的代码不是模块化的环境,定义的变量都是全局的

解决方法 :

方式1 : 写代码时 ,使用{}整体给包起来

方式2 : export {}

TS类型

介绍

TS是js的超集,TS 提供了js的所有功能,并且额外的增加了 类型系统 可以显示标记出代码中的意外行为,从而降低了发生错误的可能性

作用: 给变量添加类型约束

格式 :

  let  变量名: 类型 =初始值

示例:

 let  n : number =18   // : number 为类型注解
 
 let  num : number = '18'  // 会报错

原因 :约定变量 num 的类型为 number 类型 ,就只能给当前变量赋值为number 类型,否则就会报错

TS 的类型

TS的常用基础类型分为

js已有的类型  
   1.  原始类型:`number/string/boolean/null/undefined/symbol`
   2.  对象类型:`object`(包括,数组、对象、函数等对象)
TS新增类型
   1. 联合类型
   2. 自定义类型(类型别名)
   3. 接口
   4. 元组
   5. 字面量类型
   6. void
   ...

注意 : 1. 原始类型在TS和JS 中写法一致 2. 对象类型在TS中更加细化,每个具体的对象(比如: 数组,对象,函数.. )都有自己的类型语法

原始类型

原始类型 : number/string/boolean/null/undefined/symbol 特点 : 简单 ,这些类型完全按照JS中类型的名称来书写

  {
    // ts 分为  原始类型   新增类型
    // number 类型
    let num:number =1
    
    // undefined 类型
    let un:undefined = undefined
    
    // string 类型
    let str:string='1'
    
    // null  类型
    let nu:null=null
    
    // symbol 类型   值是唯一的
    let sym:symbol=Symbol()
    
    // Boolean  类型
    let boo:boolean=false
    
    // bigint  类型   对大整数的算术运算 保证精准度
    let big:bigint=BigInt(44)
    let b:bigint=444444444n
  }

类型推论

介绍 : 在TS 中 某些没有明确指定类型的情况下,TS 的类型推论机制会自动提供类型。好处:由于类型推论的存在,有些情况下的类型注解可以省略不写

常见的场景 : 1.声明变量并初始化时 2. 决定函数返回值时

   {
        // 类型推论
        let  age:number =4
        let  age1=8  // age1的 类型自动判断为number类型
        
        // 函数的返回值类型自动判断为 number
        function add10 (num1:number,num2:number){
           return num1+num2
        }
   }
   

联合类型

格式 :

     let  变量 : 类型1 | 类型2 | 类型3 ... = 初始值

|(竖线) 在TS 中叫做联合类型 由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种

示例 :

   let  n : number | string =1
   n='1'
   

应用场景 : 定时器id

let timer:null | number =null
timer =setTimeout(()=>{},100)

注意 : 这是TS中联合类型的写法,只有一根竖线,不要与JS中的或(|| 或)混淆了

类型别名

格式 :

type  别名 = 类型

示例:

  type NewType =string
  let  a:NewType ='abc'
  let  b:NewType='1'
  
 

作用: 1. 给类型起别名 2. 定义新类型

场景 : 给复杂类型起别名

  type NewType =string | number
  let  a:NewType =1
  let  b:NewType='1'
  
  type ar=number| string
  let  arr1:ar[] =['1',9]

注意 : 别名可以是任意的合法字符串,一般首字母大写

数组类型

格式 : 两种写法

let  变量 : 类型[] = [值1,...]

let  变量 :Array<类型> =[值1,...]

示例:

  // 写法一:
  let numbers: number[] = [1, 3, 5] //  numbers必须是数组,每个元素都必须是数字

  // 写法二:
  let strings: Array<string> = ['a', 'b', 'c'] //  strings必须是数组,每个元素都必须是字符串

复杂示例 :

// 这里有个有优先级的问题,用()包一下
let arr: (number | string) [] = []
let arr: Array<number | string> = []

// 类型推论
let arr = [1, '1']

函数类型

函数单个定义

 函数的类型 : 函数参数类型 和返回值类型
 
 格式 :
     
     // 普通函数
     function 函数名(形参1 : 类型=默认值, 形参2 : 类型=默认值):返回值类型{}
     
     // 箭头函数
     const 函数名 (形参1 : 类型=默认值,形参2 :类型 =默认值) : 返回值类型 {}
 示例:

        // 声明式
        function add(num1: number, num2: number): number {
          return num1 + num2
        }

        // 箭头函数
        const add = (num1: number, num2: number): number => {
          return num1 + num2
        }

        add(1,'1') // 报错
        

统一定义函数格式

原因 :定义多个具有相同参数类型和返回值类型的函数时,代码比较累赘

 const add = (a:number,b:number):number =>{
      return a + b
}

const sub = (a:number,b:number):number =>{
      return a - b
}

分析: 把拥有相同形参和实参的函数当做一个整体,来定义

 const add1 : (n1:number,n2:number)=>number  =  (a,b)=>{return a+b }

提炼自定义类型

 type Fn = (n1:number,n2:number) => number 
 const add1 : Fn = (a,b)=>{return a+b }

函数可选参数

场景 : 使用函数实现某个功能时,参数可以传也可以不传。

例如:数组的 slice 方法,可以 slice() 也可以 slice(1) 还可以 slice(1, 3)

这种情况下,在给函数参数指定类型时,就用到可选参数

格式 :

 可选参数:在可选参数名的后面添加 `?`(问号)

示例:

   function mySlice(start?: number, end?: number): void {
  console.log('起始索引:', start, '结束索引:', end)
}

注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数

可选和默认值的区别

相同点: 调用函数时,可以少传参数

区别:设置了默认值之后,就是可选的了,不写就会使用默认值; 可选的参数一定有值。

注意:它们不能一起使用。优先使用默认值

  // 可选参数 格式  参数 ? : 类型   可选参数不能在必选参数之前会报错  必选参数可以设置默认值 直接不用传参 使用默认值 
  // 可选参数不能设置默认值
   function  add3 (a:number=10,b?:number){
    console.log(a,b);
   }
   add3()

返回值类型void

void类型

在 TS 中,如果一个函数没有返回值,应该使用 void 类型

  function greet(name: string): void {
  console.log('Hello', name)
}

使用场景 1. return undefined 2. 直接不写return 3. 写return 不接返回内容

  // 不写return
  function add2 (a:number,b:number):void{
      console.log(a,b);
  }
   add2(1,2)
   
  // return  不给返回内容
   function add4(a?:number,b?:number):void{
      return
   }
   add4()
   
   // 直接return  undefined
   function add5 (a?:number ,b?:number):void{
      return undefined
   }
   add5()

oid和undefined的区别

如果函数没有指定返回值,调用结束之后,值是undefined的,但是不能直接声明返回值是undefined

function add(a:number, b:number): undefined { // 这里会报错
  console.log(a,b)
}

对象类型单独使用

格式 :普通函数和箭头函数

  const 对象名: {
  属性名1:类型1,
  属性名2:类型2,
  方法名1(形参1: 类型1,形参2: 类型2): 返回值类型,
  方法名2:(形参1: 类型1,形参2: 类型2) => 返回值类型
} = { 属性名1: 值1,属性名2:值2  }

可选参数 用 ?

 const 对象名: {属性名1?:类型1,属性名2:类型2 } = { 属性名2:值2 }  

示例:

   const stu:{
        name:string,
        age:number,
        // 普通函数
        hello():void,
        // 箭头函数
        run:()=>void
    }={
          name:"张三",
          age:18,
          hello:function (){
             console.log('hello');
          },
          run:()=>{
              console.log('run');

          }
    }

说明 :

  1. 使用 {} 来描述对象结构

  2. 属性采用属性名: 类型的形式,如果是多行,可以省略,

  3. 方法采用方法名(): 返回值类型的形式

  4. 可选使用 ?

对象类型-类型别名

示例 :

    type  Stu ={
        name:string,
        gender:string,
        grade:number,
        height:number,
        study():void,
        game:()=>void
    }

  const  stu1:Stu={
        name:'张三',
        gender:'男',
        grade:630,
        height:180,
        study:function(){console.log('优秀');
        },
        game:()=>{
            console.log('王者'); 
        }
  }

接口

场景 : 当一个对象类型被多次使用时,有如下两种方式来来描述对象的类型,以达到复用的目的:

  1. 类型别名,type
  2. 接口,interface

格式 :

   interface 接口名  {
    属性1: 类型1, 属性2: 类型2,
}

示例:

   interface IGoodItem  {
   name: string, price: number, func: ()=>string
}

const good1: IGoodItem = {
   name: '手表',
   price: 200,
   func: function() {
       return '看时间'
   }
}
const good2: IGoodItem = {
    name: '手机',
    price: 2000,
    func: function() {
        return '打电话'
    }
}

说明:

  1. 使用 interface 关键字来声明接口
  2. 接口名称(比如,此处的 IPerson),可以是任意合法的变量名称,推荐以 I 开头
  3. 声明接口后,直接使用接口名称作为变量的类型 接口和类型的区别: interface(接口)和 type(类型别名)的对比:

相同点:都可以给对象指定类型

不同点:

接口,只能为对象指定类型。它可以继承。

类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名

接口继承

两个接口都有 x、y 两个属性,重复写两次,可以,但很繁琐

interface IPoint2D { x: number; y: number }
interface IPoint3D { x: number; y: number; z: number }

两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用

继承格式 :

interface 接口2 extends 接口1 {
属性1: 类型1// 接口2中特有的类型
... 
}

示例:

  interface Point2D { x: number; y: number }
// 继承 Point2D
interface Point3D extends Point2D {
  z: number
}

说明:

  1. 使用 extends(继承)关键字实现了接口 Point3D 继承 Point2D
  2. 继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)

元组

元组是什么?

元组是是一种特殊的数组

元组的作用 :

  1. 约定了元素的个数
  2. 约定了特定索引对应的数据类型

实际运用 :

  {
   function setState(num:number):[number,(newNum:number)=>void]{
       const fn=(newNum:number)=>{
           console.log(newNum,'元组');
           num=newNum
       }
       return [num,fn]
    }
   const [num,setNum]=setState(100)
   setNum(80)
   console.log(num);
  }

字面量类型

let str1 = 'hello TS'
const str2 = 'hello TS'

变量 str1 的类型为:string; 变量 str2 的类型为:Hello TS

原因:

  1. str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string
  2. str2 是一个常量(const),它的值不能变化只能是 'hello TS',所以,它的类型为:'hello TS'

注意:此处的 'Hello TS',就是一个字面量类型,也就是说某个特定的字符串也可以作为 TS 中的类型

什么是字面量类型?

任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用,此时,就称为是字面量类型

常见的字面量:字面量:{ name: 'jack' } [] 18 20 'abc' false function() {}

字面量的作用 :

单个字面量没有什么用处,它一般和联合类型 一起使用, 用来表示一组明确的可选值列表

示例:

  {
    // 字面量类型  常与 联合类型 一起使用 用来表示一组明确的可选列表
    type   NewType = 'add'|'delete'|'edit'
    function handler(ac:NewType){
            if(ac==='add'){
                console.log('add');
            }else if(ac==='delete'){
                console.log('delete');
            }else if(ac==='edit'){
                console.log('edit');
            }
    }
    handler('add')
    handler('delete')
    handler('edit')
}

枚举

什么是枚举?

枚举(enum)的功能类似于字面量类型+联合类型组合的功能,来描述一个值,该值只能是 一组命名常量 中的一个.

ts中没有type之前,用枚举比较多,现在用的少了

格式:

  enum 枚举名 { 可取值1, 可取值2,.. }

说明 :

  1. 使用 enum 关键字定义枚举
  2. 一般约定首字符大写 使用格式 : 枚举名.可取值

示例 :

// 定义枚举类型
enum Direction { Up, Down, Left, Right }

// 使用枚举类型
function changeDirection(direction: Direction) {
  console.log(direction)
}

// 调用函数时,需要应该传入:枚举 Direction 成员的任意一个
// 类似于 JS 中的对象,直接通过 点(.)语法 访问枚举的成员
changeDirection(Direction.Up)

说明 :

  1. 约定枚举名称以大写字母开头
  2. 枚举中的多个值之间通过 ,(逗号)分隔
  3. 定义好枚举后,直接使用枚举名称作为类型注解

枚举的值

枚举类型和ts中其他的类型不一样,枚举类型不仅仅是类型,还是一个值。

说明 :

1.type定义的类型是没有值的

2.枚举定义的类型是有值的。 示例 :

 // type定义的类型是没有值的 - 不能console.log
type NewType = number | string
console.log(NewType)  // 输出类型是没有意义的

// 创建枚举
enum Direction { Up, Down, Left, Right }

// 枚举是有值的 - 可以console.log
console.log(Direction.Up)

数字枚举

默认情况下,枚举的值是数值。默认为:从 0 开始自增的数值. 当然,也可以给枚举中的成员初始化值

enum Direction { Up = 2, Down = 3, Left = 8, Right = 16 }
// 也可以给枚举成员设置初始值
//  enum  Dir {
//       up=10,down,left,right
//   }

字符串枚举

注意 : 字符串枚举没有自增长行为,所以 字符串枚举成员都必须设置初始值

// 枚举成员的值是字符串
// 注意 : 字符串枚举没有自增长行为,所以 字符串枚举成员都必须设置初始值
  enum  Dir {
  up='up',down='down',left='left',right='right'
  }
  let d : Dir =Dir.down
  d=Dir.right
  // 枚举定义的类型是有值的 type 是没有值的
  console.log(Dir);

小结 :

  1. 枚举与前面讲到的字面量类型+联合类型组合的功能类似,都用来表示一组明确的可选值列表
  2. 一般情况下,推荐使用字面量类型+联合类型组合的方式,因为相比枚举,这种方式更加直观、简洁、高效 应用场景 :

后端用 0, 1来标识性别,但是0,1在代码中不好读,此时可以用枚举来约定

  // 应用场景 

  enum Gender {girl,boy}

  type User ={
      name:string,
      gender:Gender
  }

  let u :User ={
      name:'小花',
      gender:Gender.girl
  }

  console.log(u);

}

Gitee