TypeScript基本使用和变量类型详解

一、TypeScript的基本使用

1.1 TypeScript的基本声明

TypeScript中定义变量需要指定变量标识符的类型。

完整的声明格式如下:

var/let/const 变量标识符:数据类型 = 需要赋的值

声明一个msg变量的代码示例:

let msg: string = "张三"		// 声明一个string类型的msg
let msg: number = 18		 // 声明一个number类型的msg

TypeScript不要使用JavaScript的包装类型,例如:String、Number、Boolean等等

1.2 变量的类型推导

有时候为了方便起见我们并不会在声明每一个变量时都写上对应的数据类型,而是希望可以通过TypeScript本身的 特性帮助我们推断出对应的变量类型。

这也是可以做到的:

// 比如声明一个msg变量, 但是并没有显示指定它的类型
let msg = "我是string类型"

// 但是再给msg赋一个number类型的值的时候,会提示报错
msg = 123

出现上面的情况是因为,ts中在变量第一次赋值时,会根据后面的赋值内容的类型,来推断出变量的类型

上面的msg是因为第一次赋值的是一个string类型,所以msg虽然没有明确说明,但是依然是string类型

二、TypeScript和JavaScript共有的数据类型

2.1 number

TypeScriptJavaScript一样,不区分整数类型(int)和浮点型 (double),统一为number类型。

let num: number = 123
let count: number = 100.01

ES6新增了二进制和八进制的表示方法,而TypeScript也支持二进制、八进制、十 六进制的表示。

let num1: number = 100 		// 十进制
let num2: number = 0b100	// 二进制
let num3: number = 0o100	// 八进制
let num4: number = 0x100	// 十六进制

2.2 boolean

JavaScript一致,只有两个取值:true或者false

let flag: boolean = true
flag = 100 > 200
flag = false

2.3 string

字符串类型,可以使用单引号或者双引号包裹内容来表示。

let message: string = 'hello typescript'

TypeScript也支持ES6的模板字符串来拼接变量和字符串。

const name: string = "张三"
const age: number = 25

let message3 = `name:${
      
      name} age:${
      
      age}`

2.4 Array

数组类型的定义有两种方式:

let content: string[] = ["a", "b", "c"]			// 开发中推荐的写法
let content: Array<string> = ["a", "b", "c"]	// 不推荐(在react的jsx中是有尖括号冲突的)

一个数组中在TypeScript开发中, 最好存放的数据类型是固定的。

不要把不同数据类型的元素放一个数组当中。

2.5 object

const info: object = {
    
    
  name: "张三",
  age: 25
}

但是从如果把info设置成object类型后,我们不能从中获取数据,也不能设置数据。

因此哪怕不加: object让TypeScript自动去推导类型,大多数情况下也不要直接使用object类型

2.6 null和undefined

JavaScript 中,undefinednull 是两个基本数据类型。

TypeScript中,它们各自的类型也是undefinednull,也就意味着它们既是实际的值,也是自己的类型。

let n1: null = null
let n2: undefined = undefined

2.7 Symbol

ES5中,是不可以在对象中添加相同的属性名称的。

要实现这个需求,通常只要定义两个不同的属性名字:比如attr1和attr2即可。

const title1 = Symbol("title")
const title2 = Symbol('title')

const info = {
    
    
  [title1]: "javascript",
  [title2]: "typescript"
}

这样一来,info对象就有了两个相同的属性title,但是一个title的值是javascript,另一个是typescript

三、TypeScript独有的数据类型

3.1 any

在某些情况下,确实无法确定一个变量的类型,并且可能它会发生一些变化,这个时候可以使用any类型。

any类型的特点:

  • 可以对any类型的变量进行任何的操作,包括获取不存在的属性、方法

  • 可以给一个any类型的变量赋值任何的值,比如数字、字符串的值

let msg: any = "Hello TypeScript"

msg = 123
msg = true
msg = {
    
    }

在不想给某些JavaScript添加具体的数据类型,就可以使用any类型

3.2 unknown

unknownTypeScript中比较特殊的一种类型,它用于描述类型不确定的变量

function Str() {
    
    
  return "message"
}

function Num() {
    
    
  return 100
}

// unknown类型只能赋值给any或者unknown类型(赋值给string,number等类型就会报错)
// any类型可以赋值给任意类型

let flag = true
let result: unknown 	//虽然这里可以使用any或者unknown,但为了安全性,最好不要使用any
if (flag) {
    
    
  result = Str()
} else {
    
    
  result = Num()
}

3.3 void

void通常用来指定一个函数是没有返回值的,即如果函数没有返回值,那么它的返回值就是void类型。

function sum(num1: number, num2: number): void {
    
    
  console.log(num1 + num2)
}

上面声明了一个函数,需要接收两个数字类型参数num1和num2,并且没有返回值。

但是即使不写void,TypeScript也会推断出来这个函数是没有返回值的,开发中习惯上是不写

3.4 never

never 表示永远不会发生值的类型,比如一个函数。

如果一个函数中是一个死循环或者抛出一个异常,那么这个函数会不会返回东西。

那么写void类型或者其他类型作为返回值类型都不合适,我们就可以使用never类型。

function foo(): never {
    
    
   // 死循环
   while(true) {
    
    

   }
}

function bar(): never {
    
    
   throw new Error()
}

never的主要应用场景:

function handleMessage(message: string | number) {
    
    
  switch (typeof message) {
    
    
    case 'string':
      console.log("处理string")
      break
    case 'number':
      console.log("处理number")
      break
    default:
      const check: never = message
  }
}

假设函数handleMessage可以接收的参数message分别为string或者numbern类型。

如果函数内部没有处理其中一个参数类型,那么最后的const check: never = message就会报错。

在一定程度上保证了程序的安全性。

3.5 tuple

tuple[])代表元组类型:代表多种元素的组合,可以知道每个元素的类型。

const info: [string, number, number] = ["why", 18, 1.88]
const name = info[0]

那么tuple和数组有什么区别:

  • 数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中(可以放在对象或者元组中) 。

  • 元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型;

tuple的应用场景:

tuple通常可以作为返回的值,在使用的时候会非常的方便.

// T代表是泛型
function useState<T>(state: T) {
    
    
  let currentState = state
  const changeState = (newState: T) => {
    
    
    currentState = newState
  }
  // (newState: T) => void 代表元组中当前元素对应的类型是有newState参数且无返回值的函数
  const tuple: [T, (newState: T) => void] = [currentState, changeState]
  return tuple
}
// 使用泛型之后,counter会是number,title会是string类型
// 相当于会自动收集类型赋值给T
const [counter, setCounter] = useState(10);
const [title, setTitle] = useState("abc")

四、TypeScript其它类型的写法

4.1 函的返回值的类型

function sum(num1: number, num2: number):number {
    
    
  return num1 + num2
}

这个类型注解出现在函数列表的后面。

通常情况下不需要返回类型注解,因为TypeScript会根据 return 返回值推断函数的 返回类型。

而某些第三方库处于方便理解,会明确指定返回类型,具体写不写看公司规范。

4.2 匿名函数的参数类型

const names = ["abc", "cba", "nba"]

names.forEach(function(item) {
    
    
  console.log(item.split(""))
})

item根据上下文的环境推导出来的, 这个时候可以不添加的类型注解
上下文中的函数: 可以不添加类型注解

4.3 对象作为函数参数

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

printPoint({
    
    x: 123, y: 321})

在这里使用了一个对象来作为类型:

  • 在对象我们可以添加属性,并且告知TypeScript该属性需要是什么类型

  • 属性之间可以使用 , 或者;来分割,最后一个分隔符是可选的

  • 每个属性的类型部分也是可选的,如果不指定,那么就是any类型

4.4 可选类型

对象类型也可以指定哪些属性是可选的,可以在属性的后面添加一个?来表示:

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

4.5 联合类型

TypeScript的类型系统允许我们使用多种运算符,从现有类型中构建新类型。

联合类型中的每一个类型被称之为联合成员(union's members);

联合类型的特点:

  • 联合类型是由两个或者多个其他类型组成的类型;

  • 表示可以是这些类型中的任何一个值;

// 普通变量的联合类型
let info: number|string = '123'

// 函数参数中的联合类型
function printID(id: number|string|boolean) {
    
    
  // 使用联合类型的值时, 需要特别注意传入的数据的类型
  // narrow: 缩小
  if (typeof id === 'string') {
    
    
    // TypeScript帮助确定id一定是string类型
    console.log(id.toUpperCase())
  } else {
    
    
    console.log(id)
  }
}

可选类型和联合类型的关系:

可选类型可以看做是某个具体类型和undefined的联合类型。

function foo(message?: string) {
    
    
    console.log(message)
}

function foo(message: string|undefined) {
    
    
     console.log(message)
}

本质上这两种达到的都是一样的效果

4.6 类型别名

在类型注解中编写对象类型和联合类型后,想要多次在其他地方使用时,就要编写多 次。

此时就可以给对象类型起一个别名,以便在别的地方进行复用。

// 定义一个类型别名
type PointType = {
    
    
  x: number
  y: number
  z?: number
}

// 使用上面定义好的类型别名
function printPoint(point: PointType) {
    
    
  
}

4.7 类型断言as

1)类型断言的基本使用

有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions)。

比如通过 document.getElementByIdts只知道该函数会返回HTMLElement,但并不知道它具体的类型。

const el = document.getElementById("imgID") 
el.src = ""

就相当于:

const el = document.getElementById("imgID") as HTMLElement
el.src = ""

此时el.src会报错,因为HTMLElement是一个比较大的范围,这上面并没有src属性。

因此el的类型并不是HTMLElement,而是一个更为具体的类型。它就是HTMLImageElement。

此时把el的类型断言为HTMLImageElement,将可以正确使用标签的相关属性。

const el = document.getElementById("why") as HTMLImageElement
el.src = ""

类型断言的作用:

把一个普遍的类型,转换为更具体的类型,从而避免一些止不可能的强制转换

2)类型断言在类中的使用

// 定义一个父类
class Person {
    
    

}

// 定义父类的一个子类,子类中包含一个自己的方法studying
class Student extends Person {
    
    
  studying() {
    
    

  }
}

// 定义一个sayHello方法,接收一个Person类型的参数
function sayHello(p: Person) {
    
    
  (p as Student).studying()		//把参数p断言成更加具体的Student类型,从而调用studying方法
}

const stu = new Student()
sayHello(stu)					//函数参数要求父类,所以继承了父类的子类也可以作为参数传递进去

如果没有断言p as Student,sayHello函数中是没有办法调用Student类中的studying方法的

因为Person中并没有studying方法

4.8 非空类型断言

当我们编写下面的代码时,在执行ts的编译阶段会报错:

这是因为传入的message有可能是为undefined的,这个时候是不能执行方法的。

// message? -> undefined | string
function printMessageLength(message?: string) {
    
    
  console.log(message.length)
}

但是,我们确定传入的参数是有值的,这个时候可以使用非空类型断言

非空断言使用的是 ! ,表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测

function printMessageLength(message?: string) {
    
    
  console.log(message!.length)
}

但是如果使用这种方式,跳过了ts的编译检测,就必须保证传入的message必须有值

4.9 可选链的使用

可选链并不是TypeScript独有的特性,它是ES11ES2020)中增加的特性。

可选链使用可选链操作符 ?.

它的作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行。

虽然可选链操作是ECMAScript提出的特性,但是在TypeScript中也可以使用。

// 定义一个ts类型
type Person = {
    
    
    name: string,
    friend? : {
    
    					// 朋友属性可以没有
        name: string	
        age? : number ,			// 朋友的age属性也可以没有
    }
}

// 定义一个Person类型的info变量
const info: Person = {
    
    
    name: "why" ,
    friend: {
    
    
    	name: "kobe"// 可以看到这里不传age属性,也是不会报错的
    }
}

// 取数据时
console.log(info.name)
console.log(info.friend?.name)	// 只有info.friend存在时,才会去调用.name,极大提示了程序的安全性
            					// 并且如果这么写info.friend.name,编译也是不通过的
            					// 因为ts认为,friend可能是没有值的,用它去.name是有安全隐患的

可选链可以省去很多的判断语句,是一个非常实用的语法

4.10 !! 操作符

!!操作符用于将一个其他类型转换成boolean类型; 类似于Boolean(变量)的的作用。

const message = "Hello World"
const flag = !!message      // 等同于 const flag = Boolean(message)
console.log(flag)

4.10 ?? 操作符

空值合并操作符(??)是一个逻辑操作符,是ES11增加的新特性。

当操作符的左侧是 null 或者 undefined时,返回其右侧操作数, 否则返回左侧操作数。

let message: string|null = "Hello World"

const info = message ?? "Hello, ??" 
// 等同于 const info = message ? message: "Hello, ??"

console.log(content)

完全就是相当于一个三元运算符

4.11 字面量类型

1)字面量类型的使用场景

某个字符串也是可以作为数据类型的,这种数据类型叫做字面量类型。

const message: "Hello World" = "Hello World"

这样的话message变量的类型就是Hello World,但是这种方式,貌似没有什么实际用途。

单单使用 字面量类型确实没有作用,但是结合联合类型就有应用空间了。

type Alignment = 'left' | 'right' | 'center'

let align: Alignment = 'left'
align = 'right'
align = 'center'

这就好像是一种枚举类型,能够限制align变量的值

2)字面量类型的推理

type Method = 'GET' | 'POST'				// 定义一个Method字面量类型,值只能为GET或者POST
function request(url: string, method: Method) {
    
    }	// 定义函数的第二个参数的参数类型为Method

type Request = {
    
    
  url: string,
  method: Method
}											// 定义Request类型

const options:Request = {
    
    					// 设置options没有的类型为Request
  url: "https://www.coderwhy.org/abc",		// 如果options没有指定类型,
  method: "POST"							// request(options.url, options.method)会报错
}											// 因为options.method的类型不在'GET'或者'POST'里

request(options.url, options.method)

五、类型缩小

什么是类型缩小?

类型缩小的英文是 Type Narrowing

可以通过类似于 typeof padding === “number” 的判断语句,来改变TypeScript的执行路径

在给定的执行路径中,可以缩小比声明时更小的类型,这个过程称之为 缩小

而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards)

常见的类型保护有如下几种:

  • typeof

  • 平等缩小(比如===!==

  • instanceof

  • in

5.1 typeof

TypeScript中,检查返回的值typeof是一种类型保护。

type IDType = number | string
function printID(id: IDType) {
    
    
  if (typeof id === 'string') {
    
    
    console.log(id.toUpperCase())
  } else {
    
    
    console.log(id)
  }
}

5.2 平等的类型缩小

type Direction = "left" | "right" | "top" | "bottom"

// 1.使用if判断来缩小类型
function printDirection1(direction: Direction) {
    
    
  
  if (direction === 'left') {
    
    
    console.log(direction)
  } else if (){
    
    
    // ...
  }
}
    
// 2.使用switch判断来缩小类型
function printDirection2(direction: Direction) {
    
    
  switch (direction) {
    
    
     case 'left':
       console.log(direction)
      break;
     case ...
  }
} 

5.3 instanceof

instanceof,如果左侧的值是右侧值的实例,则返回true

function printTime(time: string | Date) {
    
    
  if (time instanceof Date) {
    
    
    console.log(time.toUTCString())
  } else {
    
    
    console.log(time)
  }
}

5.4 in

type Fish = {
    
    
  swimming: () => void
}

type Dog = {
    
    
  running: () => void
}

function walk(animal: Fish | Dog) {
    
    
  if ('swimming' in animal) {
    
    
    animal.swimming()
  } else {
    
    
    animal.running()
  }
}

六、TypeScript中的枚举类型

枚举类型是TypeScript特有的特性之一,在 JavaScript是没有的。

枚举类型的特点:

  • 枚举就是将一组可能出现的值,一个个列举出来,定义在一个类型中

  • 枚举允许开发者定义一组命名常量,常量可以是数字、字符串类型;

枚举类型关键字:

  • 枚类型使用enum关键字定义,并且习惯上枚举类型中定义的值都是使用大写

6.1 枚举类型具体使用示例

// 定义方向的枚举类型
enum Direction {
    
    
  LEFT,
  RIGHT,
  TOP,
  BOTTOM
}


function turnDirection(direction: Direction) {
    
    
  // 打印direction就可以发现,这些枚举是有数字类型的默认值的
  console.log(direction)
  switch (direction) {
    
    
    case Direction.LEFT:
      console.log("改变角色的方向向左")
      break;
    case Direction.RIGHT:
      console.log("改变角色的方向向右")
      break;
    case Direction.TOP:
      console.log("改变角色的方向向上")
      break;
    case Direction.BOTTOM:
      console.log("改变角色的方向向下")
      break;
    default:
      const foo: never = direction;
      break;
  }
}

turnDirection(Direction.LEFT)
turnDirection(Direction.RIGHT)
turnDirection(Direction.TOP)
turnDirection(Direction.BOTTOM)

6.2 枚举类型的默认值

一般来说,函数的枚举值,会有一个数字类型的默认值

enum Direction {
    
    
  LEFT = 0,
  RIGHT = 1,
  TOP = 2,
  BOTTOM = 3
}

6.3 修改枚举类型的默认值

enum Direction {
    
    
  LEFT = 10,
  RIGHT,
  TOP,
  BOTTOM
}

如果修改第一个枚举值的默认值为10

后续其它枚举值的默认值将会从10开始递增。

enum Direction {
    
    
  LEFT = 10,
  RIGHT = 11,
  TOP = 12,
  BOTTOM = 13
}

6.4 修改其它枚举类型的默认值

enum Direction {
    
    
  LEFT = "LEFT",
  RIGHT = "RIGHT",
  TOP = "TOP",
  BOTTOM = "BOTTOM"
}

一般来讲,也不需要去修改枚举的默认值,直接使用就可以了

猜你喜欢

转载自blog.csdn.net/qq_44749491/article/details/127439175
今日推荐