类型声明


使用 :变量函数形参,进行类型声明:

语法:let或const 变量: 变量类型

示例:

let a: string // 变量a只能存储字符串  
let b: number // 变量b只能存储数值  
let c: boolean // 变量c只能存储布尔值  
  
a = 'hello'  
// a = 100 // 类型声明.ts:6:1 - error TS2322: Type 'number' is not assignable to type 'string'.  
  
b = 666  
// b = '你好' // 类型声明.ts:9:1 - error TS2322: Type 'string' is not assignable to type 'number'.  
  
c = true  
// c = 666 // 类型声明.ts:12:1 - error TS2322: Type 'number' is not assignable to type 'boolean'.  
  
const demo = (x: number, y: number):number => {  
    // return 'abc' // 类型声明.ts:15:5 - error TS2322: Type 'string' is not assignable to type 'number'.  
    return x + y  
}  
  
console.log(demo(100, 200))  
// console.log(demo(100, '200')) // 报错  
// console.log(demo(100)) // An argument for 'y' was not provide

类型推断


那这个时候就会有人要问,主播主播,我这个js的优势“弱类型”就没了呀,每次定义都需要自己定义一个类型,好麻烦呀

不用担心,TS会出手!

let d = -99 // ts 自动推断类型是数字

当然,你推断后的变量,它自动就定义了类型,如下:

let d = -99
d = false // number不能赋值布尔

类型汇总


JS中的数据类型:

  1. string
  2. number
  3. boolean
  4. null
  5. undefined
  6. bigint
  7. symbol
  8. object(包含 ArrayFunctionDateError等)

TS中的数据类型:

  1. 上述所有JS的数据类型
  2. any
  3. unknown
  4. never
  5. void
  6. tuple
  7. enum

TS自定义类型的方式:

  1. type
  2. interface

tips:
在 JavaScript 中的这些内置构造函数:NumberStringBoolean,用于创建对应的包装对象,在日常开发时很少使用,在TypeScript中也是同理,所以在 TypeScript 中进行类型声明时,通常都是用小写的numberstringboolean

例如下面代码:

let str1: string  
str1 = 'hello'  
// str1 = new String('hello') // 报错 error TS2322: Type 'String' is not assignable to type 'string'.'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible.  
let str2: String  
str2 = 'hello'  
str2 = new String('hello')  
  
console.log(typeof str1)  
console.log(typeof str2)

原始类型VS包装对象
原始类型:如 numberstringboolean,在 JavaScript 中是简单数据类型,它们在内存中占用空间少,处理速度快。
包装对象:如 Number对象、String 对象、Boolean 对象,是复杂类型,在内存中占用更多空间,在日常开发时很少由开发人员自己创建包装对象。
自动装箱:JavaScript 在必要时会自动将原始类型包装成对象,以便调用方法或访问属性

// 原始类型字符串  
let str = 'hello'  
  
// 当访问str.length时, JavaScript引擎做了以下工作:  
let size = (() => {  
    // 1. 自动装箱  
    let tempStringObject = new String(str)  
  
    // 2. 访问String对象的length属性  
    let lengthValue = tempStringObject.length  
  // 3. 销毁临时对象, 返回长度值  
    return lengthValue  
})()

常用类型与语法


any

any 的含义:任意类型,一旦变量类型限制为 any,那么就意味着放弃了对该变量的类型检查。

// 明确的表示a的类型是 any —— 【显示的any】  
let a: any  
// 以下对a的赋值, 均无警告  
a = 100  
a = '你好'  
a = false  
  
// 没有明确的表示b的类型是any, 但TS主动推断出来b是any —— 隐式的any  
let b  
// 以下对b的赋值, 均无警告  
b = 100  
b = '你好'  
b = false  
  
/* 注意点: any类型, 可以赋值给任意类型的变量 */let c: any  
c = 9  
  
let x: string  
x = c // 无警告

unknown

unknown 的含义是未知类型,适用于:“起初不确定具体数据类型,要后期才能确定”

  1. unknown 可以理解为一个类型安全的 any
// 设置a的类型为unknown  
let a: unknown  
  
// 以下对a的赋值, 军符合规范  
a = 100  
a = false  
a = '你好'  
  
// 设置x的数据类型为string  
let x: string  
x = a // 警告
  1. unknown 会强制开发者在使用之前进行类型检查,从而提供够强的类型安全性
// 设置a的类型为unknown  
let u1: unknown  
let un;  
u1 = 'hello'  
  
// 第一种方式: 加类型判断  
if (typeof u1 === 'string') {  
    un = u1  
    console.log(un)  
}  
  
// 第二种方式:加断言  
un = u1 as string  
console.log(un)  
  
// 第三种方式:加断言  
un = <string>u1  
console.log(un)
  1. 读取 any 类型数据的任何属性都不会报错,而 unknown 正好与之相反
let str1: string  
str1 = 'hello'  
str1.toUpperCase() // 无警告  
  
let str2: any  
str2 = 'hello'  
str2.toUpperCase() // 无警告  
  
let str3: unknown  
str3 = 'hello'  
str3.toUpperCase() // 警告  
(str3 as string).toUpperCase() // 断言指定为string则无警告

never

never 的含义是:任何值都不是,即“不能有任何值”,例如 undefinednull' '、`0 都不行

  1. 几乎不用never直接限制变量,因为没有意义,例如:
let Never1: never  
  
// 以下赋值均会警告  
Never1 = 1  
Never1 = true  
Never1 = undefined  
Never1 = null
  1. never一般是TyperScript主动推断出来的,例如:
// 指定类型为string  
let Never2: string  
// 设置一个值  
Never2 = 'hello'  
  
if (typeof Never2 === 'string') {  
    console.log(Never2.toUpperCase())  
} else {  
    console.log(Never2) // TS将会推断此次的Never2为 never,因为没有一个值符合逻辑  
}
  1. never也可以限制函数的返回值
const throwError= (str: string): never => {  
    throw new Error('程序异常退出: ' + str )  
   // return // 警告  
}

void

void的含义是空,即函数不返回任何值,调用者也不应依赖其返回值进行任何操作
简单记: undefined 是 void 可以接受的⼀种“空”。

  1. void通常用于函数返回值声明
const logMessage = (msg: string): void => {  
    console.log(msg)  
    // return '123' // 警告  
}  
  
logMessage('你好')
  1. 以下写法均符合规范
const logMessage0 = (msg: string): void => {  
    console.log(msg)  
}  
  
const logMessage1 = (msg: string): void => {  
    console.log(msg)  
    return  
}  
  
const logMessage2 = (msg: string): void => {  
    console.log(msg)  
    return undefined  
}  
  
logMessage0('test')  
logMessage1('test1')  
logMessage2('test2')
  1. 返回值类型为 void 的函数,调⽤者不应依赖其返回值进⾏任何操作!对⽐下两段代码,体现undefinedvoid的区别
const logMsg = (msg: string): void => {  
    console.log(msg)  
}  
  
let result = logMsg('你好')  
  
if(result) console.log('logMessage有返回值') // 报错
const logMsg1 = (msg: string): undefined => {  
    console.log(msg)  
}  
  
let result = logMsg1('你好')  
  
if(result) console.log('logMessage有返回值') // 不报错

object和Object

object

object (⼩写)的含义是:所有⾮原始类型,可存储:对象、函数、数组等,由于限制

的范围⽐较宽泛,在实际开发中使⽤的相对较少。

Object

  • 官⽅描述:所有可以调⽤ Object ⽅法的类型。
  • 简单记忆:除了 undefined 和 null 的任何值。
  • 由于限制的范围实在太⼤了!所以实际开发中使⽤频率极低。

声明对象类型

  1. 在实际开发中,限制一般对象,通常使用以下形式
// 限制person1对象必须有name属性, age为可选属性  
let person1: { name: string, age?: number }  
  
// 含义同上, 也能用分号做分隔  
let person2: { name: string; age?: number }  
  
// 含义同上, 也能用换行做分隔  
let person3: {  
    name: string  
    age?: number  
}  
  
// 以下赋值均可  
person1 = { name: 'lisi', age: 18 }  
person2 = { name: 'zhangsan' }  
  
// 如下赋值不合法  
person3 = { name: 'wangwu', gender: '男'}
  1. 索引签名:允许定义对象具有任意数量的属性,这些属性的类型可变的,常用于:描述类型不确定的属性,(具有动态属性的对象)。
// 限制person对象必须有name属性,可选age属性但值必须是数字,同时可以有任意数量、任意类型的其他属性  
let person: { name: string; age: number; gender: string }  
  
// 赋值合法  
person = {  
    name: '张三',  
    age: 18,  
    gender: '男'  
}

声明函数类型

let count: (a: number, b: number) => number  
  
count = (x, y) => {  
    return x + y  
}  
  
console.log(count(1, 2))

tips:
TypeScript中的 => 在函数声明时表示函数类型,描述其参数类型返回类型
JavaScript中的 => 是一种定义函数的语法,是具体函数实现。

声明数组类型

let arr1: string[]  
let arr2: Array<string>  
  
arr1 = ['a', 'b', 'c']  
arr2 = ['hello', 'world']  
  
console.log(arr1, arr2)

tuple

元组 (Tuple) 是⼀种特殊的数组类型,可以存储固定数量的元素,并且每个元素的类型是已知的且可以不同。元组⽤于精确描述⼀组值的类型, ? 表示可选元素。

// 第一个元素必须是 string 类型  
let arr1: [string, number]  
// 第一个元素必须是 number 类型,第二个元素是可选的,如果存在,必须是 boolean 类型  
// @ts-ignore  
let arr2: [number, boolean?]  
// 第一个元素必须是 number 类型,后面的元素可以是任意数量的 string 类型  
let arr3: [number, ...string[]]  
  
// 可以赋值  
arr1 = ['hello', 123]  
arr2 = [100, false]  
arr2 = [200]  
arr3 = [100, 'hello', 'world']  
arr3 = [100]  
  
// 不可以赋值  
arr1 = ['hello', 123, false]

enum

枚举(enum)可以定义一组命名常量,它能增强代码的可读性,也可以让代码更好维护

如下代码的功能是:根据调⽤ walk 时传⼊的不同参数,执⾏不同的逻辑,存在的问题是调⽤ walk 时传参时没有任何提示,编码者很容易写错字符串内容;并且⽤于判断逻辑的 up 、 down 、 left 、 right 是连续且相关的⼀组值,那此时就特别适合使⽤ 枚举( enum )

const walk = (str: string):void => {  
    if (str === 'up') console.log('向【上】走')  
    else if (str === 'down') console.log('向【下】走')  
    else if (str === 'left') console.log('向【左】走')  
    else if (str === 'right') console.log('向【右】走')  
    else console.log('未知方向')  
}  
  
walk('up')  
walk('down')  
walk('left')  
walk('right')

数字枚举

数字枚举是最常见的枚举类型,其成员的值会自动递增,且数字枚举还具备反向映射的特点,以下代码展示:

// 定义一个描述【上下左右】方向的枚举Direction  
enum Direction {  
    Up,  
    Down,  
    Left,  
    Right  
}  
  
console.log(Direction)  
/*  
Direction输出内容:  
  
{  
  '0': 'Up',  '1': 'Down',  '2': 'Left',  '3': 'Right',  Up: 0,  Down: 1,  Left: 2,  Right: 3}  
  
*/  
  
// 反向映射  
console.log(Direction.Up)  
console.log(Direction[0])  
  
// Direction.Up = 'shang' // 报错,枚举中的属性是只读的

通过上方代码可知:【可以通过来获取枚举成员名称

也可以指定枚举成员的初始值,其后的成员值会自动递增

enum Direction {  
    Up = 6,  
    Down,  
    Left,  
    Right  
}  
  
console.log(Direction.Up) // 6
console.log(Direction.Down) // 7

回到最开始的程序,我们使用枚举改造一下:

enum Direction {  
    Up,  
    Down,  
    Left,  
    Right  
}  
  
const walk = (n: Direction): void => {  
    if (n === Direction.Up) console.log("向【上】走")  
    else if (n === Direction.Down) console.log("向【下】走")  
    else if (n === Direction.Left) console.log("向【左】走")  
    else if (n === Direction.Right) console.log("向【右】走")  
    else console.log("未知方向")  
}  
  
walk(Direction.Up)  
walk(Direction.Down)

字符串枚举

枚举成员的值是字符串

enum Direction {  
    Up = "up",  
    Down = "down",  
    Left = "left",  
    Right = "right"  
}  
  
let dir: Direction = Direction.Up  
console.log(dir) // up

常量枚举

使用普通枚举的TS代码:

enum Directions {
	Up,
	Down,
	Left,
	Right
}

let x = Directions.Up;

编译生成后的JS代码量较大:

use strict";
var Directions;
(function (Directions) {
	Directions[Directions["Up"] = 0] = "Up";
	Directions[Directions["Down"] = 1] = "Down";
	Directions[Directions["Left"] = 2] = "Left";
	Directions[Directions["Right"] = 3] = "Right";
})(Directions || (Directions = {}));

let x = Directions.Up;

使用常量枚举TS代码如下:

const enum Directions {
	Up,
	Down,
	Left,
	Right
}

let x = Directions.Up;

编译后生成的JS代码量较小:

"use strict";

let x = 0 /* Directions.Up */;

TS自定义类型


type

type 可以为任意类型创建别名,让代码更简洁、可读性更强,同时能更方便地进行类型复用和拓展。

基本用法

类型别名使用 type 关键字定义,type 后跟类型名称,如下:

type num = number

let price: num
price = 100

联合类型

联合类型是一种高级类型,它表示一个值可以是几种不同类型之一。

type Status = number | string  
type Gender = '男' | '女'  
  
const printStatus = (status: Status): void => {  
    console.log(status)  
}  
  
const logGender = (str: Gender): void => {  
    console.log(str)  
}  
  
printStatus(404)  
printStatus('200')  
printStatus('501')  
// printStatus(true) // 报错  
  
logGender('男')  
logGender('女')  
// logGender('a') // 报错

交叉类型

交叉类型(Intersection Types)允许将多个类型合并为一个类型。合并后的类型将拥有所有被合并类型的成员。交叉类型通常用于对象类型。

// 面积  
type Area = {  
    height: number // 高  
    width: number  
}  
  
// 地址  
type Address = {  
    num: number // 楼号  
    cell: number // 单元号  
    room: string // 房间号  
}  
  
// 定义类型House,且House是Area和Address组成的交叉类型  
type House = Area & Address  
  
const house: House = {  
    height: 180,  
    width: 75,  
    num: 6,  
    cell: 3,  
    room: '701'  
}  
  
console.log(house)
一个特殊情况

代码段1(正常)

function demo():void{

// 返回undefined合法

return undefined

// 以下返回均不合法

return 100

return false

return null

return []

}

demo()

代码段2(特殊)

type LogFunc = () => void

const f1: LogFunc = () => {

return 100; // 允许返回⾮空值

};

const f2: LogFunc = () => 200; // 允许返回⾮空值

const f3: LogFunc = function () {

return 300; // 允许返回⾮空值

};

const src = [1, 2, 3];

const dst = [0];

src.forEach((el) => dst.push(el));