当前位置: 首页 > article >正文

TypeScript (一)运行环境配置,数据类型,可选类型,联合类型,type与interface,交叉类型,断言as,字面量类型,类型缩小

文章目录

  • 一、认识TS
    • 1.1 JS 存在的问题
    • 1.2 TS的出现
    • 1.3 TS运行环境
      • 运行ts的三种方式
    • 1.4 变量声明
    • 1.5 类型推断
  • 二、数据类型
    • 2.1 JS数据类型
      • (1) 数组Array
      • (2) 对象Object
      • (3) 其他类型
    • 2.2 TS特有数据类型
      • (1) any类型
      • (2) unknown类型
      • (3) void类型
      • (4) never (了解)
      • (5) tuple类型
  • 三、语法细节
    • 3.1 可选类型
    • 3.2 联合类型
    • 3.3 type与interface
      • (1) 类型别名 type
      • (2) 声明接口 interface
      • (3) 区别
    • 3.4 交叉类型
    • 3.5 类型断言as
    • 3.6 字面量类型
    • 3.7 类型缩小 (Type Narrowing)

一、认识TS

1.1 JS 存在的问题

JS没有类型检测。

function getLength(str){
  return str.length;
}
console.log(getLength("abc"));  // 正确调用,结果是3
console.log(getLength(123));    // 错误的调用,结果是undefined;

第五行代码在编写时不会报错,但是在运行过程中可能会出现TypeError: Cannot read property 'length' of undefined这类的错误。这是因为JS并不会对函数传入的参数进行限制,所以这个问题只会在运行时才被发现。进而影响后续代码的执行;

所以没有类型监测带来的问题:

  • 代码不够安全、不够健壮。
  • 没有类型约束,那么需要对别人传入的参数进行各种验证来保证我们代码的健壮性
  • 我们去调用别人的函数,对方没有对函数进行任何的注释,我们只能去看里面的逻辑来理解这个函数需要传入什么参数, 返回值是什么类型;

1.2 TS的出现

TypeScript官网:TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.

TypeScript是拥有类型的JavaScript超集,它可以编译成普通、干净、完整的JavaScript代码。

TS是加强版的JS,具有以下特点:

  • JavaScript所拥有的特性,TypeScript全部都是支持的,
  • 紧随ECMAScript的标准,所以ES6、ES7、ES8等新语法标准,它都是 支持的;
  • TS增加了类型约束
  • 不必考虑兼容性问题。TS最终会被编译成JS代码,编译时可以通过tsc或babel将其转为js(始于JS,归于JS)

1.3 TS运行环境

在.ts文件里编写TS不会出错,但是编译时需要搭建对应的环境,将其编译为JavaScript,才能够在浏览器上运行。

# 全局安装最新版本的 ts
npm install typescript -g

# 检查版本,查看是否安装成功
tsc --version

运行ts的三种方式

(1) 方式一:
第一步: tsc 1.hello.ts ; 编译ts文件,生成1.hello.js文件
第二步:运行.js文件
每次修改完文件后,都要单独执行命令进行编译,然后再运行。很麻烦。

(2) 方式二:

  • webpack配置(用脚手架创建文件的时候都会配置好,这里不细说)

(3) 方式三:

  • 通过ts-node库 (接下来学习用这种方式来运行ts文件,)。
# 安装ts-node
npm install ts-node -g
# 安装ts-node需要的两个依赖包:tslib和@types/node
npm install tslib @types/node -g
# 运行ts文件,这种方式也不需要每次修改完之后单独执行命令进行编译,直接执行即可
ts-node hello.ts

注意:在使用ts-node运行代码时,控制台报错:Cannot find module ‘./hello.ts’

解决方法:添加export {}

// hello.ts
let message: string = "hello world"
console.log(message)
export {}

博客:使用ts-node命令运行ts文件时报错

1.4 变量声明

声明了类型后TypeScript就会进行类型检测,声明的类型可以称之为类型注解(Type Annotation)

(指明数据类型就是添加类型注解)

var/let/const 标识符:数据类型 = 赋值

将变量message声明为string类型:

let message:string = 'hello'
message = 123  // 将其他类型赋值给message会报错:不能将类型"number"分配给类型"string"

1.5 类型推断

类型推断:声明一个变量时,如果直接进行赋值, 会根据赋值的类型推导出变量(标识符)的类型注解。

let name='tom'  // 推导出name的类型为string
name=123       // 这句赋值操作会报错:不能将“number”分配给类型"string"
let num = 123

const msg = 123  // 推导出msg的类型为字面量类型123
const test:'Hello'='Hello' // 这里'Hello' 也是字面量类型

注:

  • let声明的变量进行类型推导,推导出来的是通用类型(number、string之类的)
  • const声明的变量进行类型推导,推导出来的是字面量类型

二、数据类型

TS是JS的一个扩展,原来JS拥有的东西,TS都有,包括JS里的八种数据类型,TS也是可以使用的。
接下来演示一下如何使用各种数据类型进行数据注解。

2.1 JS数据类型

(1) 数组Array

指定数组的类型注解,有两种写法

(1) string:[]——数组类型,并且数组里只能存string类型数据

const msg:string[]=["123","345","567"]
msg.push('hello')

(2) Array<string>——泛型写法,

const names :Array<string>=["tom","jerry"]
names.push(876) // 会报错

简写就是:

const name = ["tom","Lily","张三"]
name.push("jerry")
name.push(123)  // 会报错

(2) 对象Object

info对象的类型就是红框里的样子
在这里插入图片描述

明确指定为对象类型的完整写法为:

const info:{
  name:string,
  age:number
}={
  name:'tom',
  age:18
}

还有一种写法,info:object={...},缺点是不能读取对象内的属性
在这里插入图片描述

(3) 其他类型

number类型

// number数据类型,ts与js一致,不区分整数和浮点数
let num = 6.66;
num = 'tom' // 会报错
// ES6新增的各个进制的表示方法,ts也适用
num = 10;     // 十进制
num  = 0b110; // 二进制
num  = 0o555; // 八进制
num  = 0xf23; // 十六进制

boolean类型

let flag = true
flag =20  // 会报错

string类型

const name = "tom"
// ts也可使用ES6的模板字符串来拼接变量和字符串
const info = `my name is ${name}`

null与undefined类型

// 完整写法
let n:null = null
let u:undefined = undefined
// 简写
let n = null
let u = undefined

2.2 TS特有数据类型

(1) any类型

在某些情况下,无法确定某些变量的类型,且该变量的类型可能会发生改变时,可采用any类型。

let id:any = 987
id = 'tom'
id = {name:'why',age:10}
// 若需定义一个数组,但不确定元素类型,可定义为any:
const infos:any[] = ['123',123,{}]

这样其实是回归到原始的js了。

应用场景

如果对于某些情况的处理过于繁琐不希望添加规定的类型注解,或者在引入一些第三方库时,缺失了类型注解,这个时候我们可以使用any。

(2) unknown类型

与any作用类似,在不确定类型的情况下也可以声明为unknown类型,区别是:

unknown类型的变量,不允许进行任何操作

let num:any=123
num = '123'
console.log(num+123); // any类型数据,不报错

let result:unknown='tom'
result = 123                // 将number类型数据赋值给result不会报错   
console.log(result.length); // 读取length属性,报错

因为不确定result的什么类型,读取length可能会出错,所以unknown类型数据不允许进行任何操作。

若想进行操作,则必须进行类型校验:

let result:unknown='tom'
result = 123    
// 必须进行类型校验(类型缩小),才能根据缩小之后的类型,进行对应的操作
if (typeof result === 'string') {
  console.log(result.length); 
}

(3) void类型

  • 当一个函数没有return返回值的时候,其返回值就是void类型
// 未指明返回值类型,默认
function printID(id:string){
  console.log(id);
}
  • 明确指明返回值类型是void;
function printID(id:string):void{
  console.log(id);
  return undefined // 对于返回值为void类型的函数,可以return undefined;
  // return 123    会报错,不能将number类型分配给void类型
}
  • (了解)基于上下文类型推导的函数,推导出返回类型是void时,并不会强制函数一定不能返回内容
    在这里插入图片描述

    但如果是自己编写的函数,明确指定是void类型,图片里13行的代码则会报错

(4) never (了解)

never 表示永远不会发生值的类型。开发中很少定义never类型。

应用场景: 进行类型推导时,可能会自动推导出never类型

function handleMsg(msg:string | number){
  switch(typeof msg){
  case "string":
    console.log(msg.length);
    break;
  case "number":
    console.log(msg+10);
    break;
  default:
    const check = msg // const check: never
  }
}

msg只可能是string或number类型,default语句不可能被执行到,所以check被推导为never类型。

(5) tuple类型

tuple元组类型。元组数据结构可以存放不同的数据类型,取出来的item也是有明确的类型

const msg:(string|number)[]=['tom',123,456]
const name = msg[0] // name类型是string|number,不能明确

const info:[string,number,boolean]=["why",18,true] // 元素的类型应与声明的一一对应,比如第二个必须是number类型的数据,否则报错
const value = info[2] // value类型是boolean
  • tuple和数组的区别:
    • 数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中。
    • 元组中每个元素都有自己特定的类型,根据索引值获取到的值可以确定对应的类型;

元组类型的应用:在函数中使用最多,尤其是函数的返回值中

// 将函数会返回值声明为tuple类型
function useState(initialValue:any):[number,(newValue:number)=>void]{
let stateValue = initialValue
function setValue(newValue:any){
  stateValue = newValue
}
return [stateValue,setValue]
}
const [count,setCount] = useState(10)
count()     // 这行代码会报错,count类型是number,错误的使用会及时提醒
setCount(100)

但若未明确指定返回值的类型,则推导为any类型的数组:
在这里插入图片描述
此时count()语句不合法,但没有错误提示,则代码是不安全的。

三、语法细节

3.1 可选类型

可以指定某个对象中某个属性是否是可选的(可以不传),语法就是加个问号?

function printPoint(point: { x: number; y: number; z?: number }) {
  console.log(point.x)
  console.log(point.y)
  console.log(point.z)
}

// printPoint({ x: 123}) // 报错,因为y是必传的但没传
printPoint({ x: 123, y: 321 }) // 123 321 undefined
printPoint({ x: 123, y: 321, z: 111 }) // 123 321 111

3.2 联合类型

TS中,我们可以使用运算符从现有类型中构建新类型

联合类型(Union Type):有两个或更多其他类型组成的新类型;表示变量类型可以是这些类型中的任意一个。

// foo可以是number类型,也可以是string类型
let foo : number | string = "abc"
foo = 123

给一个联合类型的变量赋值:只要保证是联合类型中的某一个类型的值即可。拿到值之后,最好需要进行类型缩小,判断出更具体的数据类型,再进行下一步的操作。

function printId(id:string|number){
  // 类型缩小
  if (typeof id === 'string') {
    console.log('id是',id.toUpperCase());
  }else {
    console.log('id是',id);
  }
}
printId(123)    // id是123
printId('tom')  // id是TOM

3.3 type与interface

(1) 类型别名 type

某些类型名字很长,且在多处使用,可以给该类型起一个别名。

比如:

type IDtype = number | string
function printId(id:IDtype) {...}
//等价于
function printId(id:number | string) {...}

再比如:

type PointType = {
  x:number,
  y:number,
  z?:number
}
function printPoint(point: PointType) {...}
// 等价于
function printPoint(point: {x:number,y:number,z?:number}) {...}

起别名之后,可读性会更强一点,使用也更方便一点。

(2) 声明接口 interface

interface的作用和type相似,都可以用来 给对象类型起别名。interface的使用方式为:

// 通过接口interface 创建某种对象类型----是一种声明的方式
interface PointType{
  x:number,
  y:number,
  z:number
 }
// 别名type创建某种对象类型----是一种赋值的方式
 type PointType = {
  x:number,
  y:number,
  z:number
}

接口的几乎所有特性都可以在 type 中使用。

(3) 区别

  • 一、type类型的使用范围更广,接口类型只能用来声明对象

    type MyNumber = number
    type IDtype = number | string
    
  • 二、声明对象时,interface可以多次声明,

    • type不允许有两个相同名称的别名

      type PointType = {
        x:number
        y:number
      }
      type PointType ={  // 提示标识符PointType重复
        z:number
      }
      
    • interface可以多次声明同一个接口

      interface PointType{
        x:number
        y:number
      }
      interface PointType{
        z:number
      }
      // 这几次声明里的条件,该类型的变量都需要满足
      const point:PointType={
        x:20,
        y:20,
        z:40
      }
      
  • 三:interface支持继承

    interface IAnimal{
      name:string,
      age:number
    }
    interface IDog extends IAnimal{
      color: string
    }
    // 变量dog需要给这三个属性都赋值
    const dog: IDog={
      name:'tom',
      age:2,
      color:'red'
    }
    
  • 四、interface可以被类实现(涉及到TS面向对象)

    class Person implements IAnimal{}
    

总结:从代码的扩展性角度来说,如果是对象类型的声明,应使用interface,非对心类型的定义应使用type。

3.4 交叉类型

联合类型:值为多个类型中的一个即可

交叉类型:需要同时满足多个类型的条件,使用&

type MyType = string & number 

上述代码的含义是MyType类型的变量既是string,又是number;不可能存在这样的数据,所以:交叉类型通常是对对象类型进行交叉的:

interface IMan{
  name:string,
  age:number
}
interface ICoder{
  name:string
  coding:()=>void
}
type NewType = IMan & ICoder
const obj: NewType={
  name:'tom',
  age:23,
  coding(){
    console.log('coding');
  }
}

3.5 类型断言as

在确定具体类型时,直接使用类型断言来指明类型,减少类型缩小代码的编写。

// 当选择标签选择器时,ele和ele2的类型是确定的
const ele  = document.querySelector('div')  // const ele: HTMLDivElement
const ele2 = document.querySelector('img')  // const ele2: HTMLImageElement
ele2.src="..."  //且可以给src属性赋值

// 采用类选择器时,ele3的类型则是const ele3: Element,没那么具体了
const ele3 = document.querySelector('.img')
ele3.src=""    // 会报错,提示Element不存在属性src

如果我们确定ele3一定是HTMLImageElement类型,则可以使用断言来具体类型

const ele3 = document.querySelector('.img') as HTMLImageElement
ele3.src=""  // 断言之后也不会报错

TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本

// 代码本身不正确,不建议这样写。这里是为了说明应断言为更加具体或不太具体的类型
const age:number = 18
const age1 = age as any  // 断言为不太具体的类型 number->any
const age2 = age1 as string // 断言为更具体地类型 any->string
console.log(age2.length);

// number断言为string类型,会报错
const age3 = age as string

3.6 字面量类型

字面量类型(literal types)

// const类型的变量可自动推断为字面量类型; let也可以声明字面量类型的变量
const name = 'tom'        // name类型为"tom"
let name2:'tom' = 'tom'   // name2类型为"tom"
// 字面量类型的应用通常是将多个字面量类型联合
type Direction = "left" | "right" | "up" | "down"
const d1: Direction = "left"

案例:

const info={
  url:"xxx",
  method:"GET"
}
function request(url:string,method:"GET"|"POST"){
  console.log(url,method);
}

request(info.url,info.method) // 会报错

原因:method的类型是string,值是GET;而request函数要求参数method的类型是"GET"或者"POST"

解决:

// 方案一:类型断言
request(info.url,info.method as "GET")

// 方案二:直接让info对象类型是一个字面量类型
const info={
  url:"xxx",
  method:"GET"
} as const
request(info.url,info.method)

在这里插入图片描述

为什么xxx也可以,因为xxx本身是一个string(不懂)

3.7 类型缩小 (Type Narrowing)

类型缩小有以下几种方式:typeof、平等缩小(===,!=)、instance of、in、等。。

  • typeof

    function printId(id:number|string){
      if (typeof id === 'string') {
        console.log(id.toUpperCase());
      }else{
        console.log(id);
      }
    }
    
  • 平等缩小 ,主要用于字面量类型

      type Direction = "left" | "right"
      function turnDirection(direction:Direction){
        switch(direction){
          case 'left':
            console.log('left'); break;
          case 'right':
            console.log('right'); break
          default:
            console.log('调用默认方法');      
        }
      }
    
  • instanceof 检查是否为另一个值的实例

    function printValue(date: Date | string) {
        if (date instanceof Date) {
            console.log(date.toLocaleDateString());
        }else{
            console.log(date);
        }
    }
    

    if语句里也可以用typeof, 但是 typeof date === 'Date' 是不对的; Date 属于object 可以写成typeof date === 'object'

  • in用于确定对象是否具有某个属性
    在这里插入图片描述


http://www.kler.cn/a/422051.html

相关文章:

  • 知乎大数据开发面试题及参考答案
  • C# 动态类型 Dynamic
  • etcd-v3.5release-(2)-STM
  • 【浏览器】缓存与存储
  • 30分钟学会正则表达式
  • 【C语言】结构体(四)
  • 【青牛科技】超低功耗混合信号处理器由多种设备组成的流量计电路芯片——D8331
  • Linux之信号集基础
  • 怎么自己创建一个网站? 开发语言首选 java,使用CMS网站内容管理系统是不错的选择
  • Redis设计与实现第17章 -- 集群 总结2(执行命令 重新分片)
  • 能源投资工程VS智能驾驶,DolphinScheduler如何当好传统行业与前沿科技的桥梁?
  • [网络] UDP通信接口及一些简易项目
  • vue 通过 mqtt 实现实时接收消息
  • 算法笔记:力扣24. 两两交换链表中的节点
  • centos7下安装promethus及grafana
  • flutter 报错 error: unable to find git in your path.
  • MongoDB注入攻击测试与防御技术深度解析
  • 若依前端问题
  • redis针对string的命令及应用场景
  • API 数据接口使用与安全指南
  • vitess使用记录:vtctldclient,设置分表规则
  • Postgres 如何使事务原子化?
  • [每周一更]-(第125期):模拟面试|NoSQL面试思路解析
  • 备赛蓝桥杯--算法题目(2)
  • 基于Matlab地形和环境因素的森林火灾蔓延模拟与可视化研究
  • Windows系统搭建Docker