首页 Order Typescript 正文

Typescript 类型

金鹏头像 金鹏 Typescript 2024-10-07 10:10:14 0 190
导读:类型总览JavaScript中的数据类型  ①string②number③boolean④null⑤undefined⑥bigint⑦symbol⑧obje...

类型总览

JavaScript 中的数据类型   

①string

②number

③boolean

④null

⑤undefined

⑥bigint

⑦symbol

⑧object

备注:其中object包含:Array、Function、Date、Error等......


TypeScript 中的数据类型   


1. 上述所有 JavaScript 类型 

2. 六个新类型:

①any

②unknown

③never

④void

⑤ tuple

⑥ enum


3. 两个用于自定义类型的方式:


①type

②interface


注意点!   

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



例如下面代码:

let str1: string

str1 = 'hello'

str1 = new String('hello') //报错


let str2: String

str2 = 'hello'

str2 = new String('hello')


console.log(typeof str1)

console.log(typeof str2) 



1. 原始类型 VS 包装对象


· 原始类型:如number、string、boolean,在 JavaScript 中是简单数据类型,它们在内存中占用空间少,处理速度快。

· 包装对象:如Number对象、String对象、Boolean对象,是复杂类型,在内存中占用更多空间,在日常开发时很少由开发人员自己创建包装对象。


2. 自动装箱:JavaScript 在必要时会自动将原始类型包装成对象,以便调用方法或访问属性

// 原始类型字符串

let str = 'hello';


// 当访问str.length时,JavaScript引擎做了以下工作:


let size = (function() {


  // 1. 自动装箱:创建一个临时的String对象包装原始字符串

  let tempStringObject = new String(str);



  // 2. 访问String对象的length属性

  let lengthValue = tempStringObject.length;



  // 3. 销毁临时对象,返回长度值

  // (JavaScript引擎自动处理对象销毁,开发者无感知)


  return lengthValue;


})();



console.log(size); // 输出: 5 



常用类型与语法

1. 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类型的变量,可以赋值给任意类型的变量


/* 注意点:any类型的变量,可以赋值给任意类型的变量 */


let c:any

c = 9

let x: string


x = c // 无警告

2. unknown


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

1. unknown可以理解为一个类型安全的any。


// 设置a的类型为unknown

let a: unknown 


//以下对a的赋值,均符合规范

a = 100

a = false

a = '你好'


// 设置x的数据类型为string


let x: string

x = a //警告:不能将类型“unknown”分配给类型“string”


  

2. unknown会强制开发者在使用之前进行类型检查,从而提供更强的类型安全性。 


// 设置a的类型为unknown

let a: unknown

a = 'hello'


//第一种方式:加类型判断

if(typeof a === 'string'){


  x = a 


  console.log(x)


}



//第二种方式:加断言

x = a as string



//第三种方式:加断言

x = <string>a


  


3. 读取any类型数据的任何属性都不会报错,而unknown正好与之相反。

let str1: string

str1 = 'hello'

str1.toUpperCase() //无警告



let str2: any

str2 = 'hello'

str2.toUpperCase() //无警告



let str3: unknown

str3 = 'hello';

str3.toUpperCase() //警告:“str3”的类型为“未知”



// 使用断言强制指定str3的类型为string

(str3 as string).toUpperCase() //无警告


  

3. never

never的含义是:任何值都不是,即:不能有值,例如undefined、null、''、0都不行!

1. 几乎不用never去直接限制变量,因为没有意义,例如:


/* 指定a的类型为never,那就意味着a以后不能存任何的数据了 */

let a: never



// 以下对a的所有赋值都会有警告

a = 1 

a = true

a = undefined

a = null


  

2. never一般是TypeScript主动推断出来的,例如:

// 指定a的类型为string

let a: string


// 给a设置一个值

a = 'hello'


if (typeof a === 'string') {


  console.log(a.toUpperCase())


} else {


  console.log(a) // TypeScript会推断出此处的a是never,因为没有任何一个值符合此处的逻辑


}


  


3. never也可用于限制函数的返回值


// 限制throwError函数不需要有任何返回值,任何值都不行,像undeifned、null都不行

function throwError(str: string): never {


  throw new Error('程序异常退出:' + str)


}


  

4. void

void的含义是空,即:函数不返回任何值,调用者也不应依赖其返回值进行任何操作!


1. void通常用于函数返回值声明

function logMessage(msg:string):void{


  console.log(msg)


}


logMessage('你好')


注意:编码者没有编写return指定函数返回值,所以logMessage函数是没有显式返回值的,但会有一个隐式返回值 ,是undefined,虽然函数返回类型为void,但也是可以接受undefined的,简单记:undefined是void可以接受的一种“空”。



2. 以下写法均符合规范


// 无警告

function logMessage(msg:string):void{


  console.log(msg)


}



// 无警告

function logMessage(msg:string):void{


  console.log(msg)


  return;


}



// 无警告

function logMessage(msg:string):void{


  console.log(msg)


  return undefined


}


  


3. 那限制函数返回值时,是不是undefined和void就没区别呢?—— 有区别。因为还有这句话 :【返回值类型为void的函数,调用者不应依赖其返回值进行任何操作!】对比下面两段代码:

function logMessage(msg:string):void{


  console.log(msg)


}



let result = logMessage('你好')



if(result){ 

  // 此行报错:无法测试 "void" 类型的表达式的真实性

  console.log('logMessage有返回值')


}


function logMessage(msg:string):undefined{


  console.log(msg)


}



let result = logMessage('你好')



if(result){ // 此行无警告


  console.log('logMessage有返回值')


}


   

理解 void 与 undefined

· void是一个广泛的概念,用来表达“空”,而 undefined 则是这种“空”的具体实现。

· 因此可以说 undefined是void能接受的一种“空”的状态。

· 也可以理解为:void包含undefined,但void所表达的语义超越了undefined,void是一种意图上的约定,而不仅仅是特定值的限制。 


总结:

如果一个函数返回类型为void,那么:

1. 从语法上讲:函数是可以返回undefined的,至于显式返回,还是隐式返回,这无所谓!

2. 从语义上讲:函数调用者不应关心函数返回的值,也不应依赖返回值进行任何操作!即使我们知道它返回了undefined。



5. object

关于object与Object,直接说结论:实际开发中用的相对较少,因为范围太大了。

object(小写)   

object(小写)的含义是:所有非原始类型,可存储:对象、函数、数组等,由于限制的范围比较宽泛,在实际开发中使用的相对较少。

let a:object //a的值可以是任何【非原始类型】,包括:对象、函数、数组等



// 以下代码,是将【非原始类型】赋给a,所以均符合要求

a = {}

a = {name:'张三'}

a = [1,3,5,7,9]

a = function(){}

a = new String('123')

class Person {}

a = new Person()



// 以下代码,是将【原始类型】赋给a,有警告

a = 1        // 警告:不能将类型“number”分配给类型“object”

a = true     // 警告:不能将类型“boolean”分配给类型“object”

a = '你好'   // 警告:不能将类型“string”分配给类型“object” 

a = null     // 警告:不能将类型“null”分配给类型“object”

a = undefined // 警告:不能将类型“undefined”分配给类型“object”


Object(大写)   

· 官方描述:所有可以调用Object方法的类型。

· 简单记忆:除了undefined和null的任何值。

· 由于限制的范围实在太大了!所以实际开发中使用频率极低。


let b:Object //b的值必须是Object的实例对象(除去undefined和null的任何值)


// 以下代码,均无警告,因为给a赋的值,都是Object的实例对象

b = {}

b = {name:'张三'}

b = [1,3,5,7,9]

b = function(){}

b = new String('123')

class Person {}

b = new Person()

b = 1 // 1不是Object的实例对象,但其包装对象是Object的实例

b = true // truue不是Object的实例对象,但其包装对象是Object的实例

b = '你好' // “你好”不是Object的实例对象,但其包装对象是Object的实例


// 以下代码均有警告

b = null     // 警告:不能将类型“null”分配给类型“Object”

b = undefined // 警告:不能将类型“undefined”分配给类型“Object”


声明对象类型   

1. 实际开发中,限制一般对象,通常使用以下形式 

// 限制person1对象必须有name属性,age为可选属性

let person1: { name: string, age?: number }


// 含义同上,也能用分号做分隔

let person2: { name: string; age?: number }



// 含义同上,也能用换行做分隔

let person3: {


  name: string


  age?: number


}



// 如下赋值均可以

person1 = {name:'李四',age:18}

person2 = {name:'张三'}

person3 = {name:'王五'}



// 如下赋值不合法,因为person3的类型限制中,没有对gender属性的说明

person3 = {name:'王五',gender:'男'}


   


2.  索引签名: 允许定义对象可以具有任意数量的属性,这些属性的键和类型是可变的,常用于:描述类型不确定的属性,(具有动态属性的对象)。 


// 限制person对象必须有name属性,可选age属性但值必须是数字,同时可以有任意数量、任意类型的其他属性

let person: {


  name: string


  age?: number


  [key: string]: any // 索引签名,完全可以不用key这个单词,换成其他的也可以


}



// 赋值合法

person = {


  name:'张三',


  age:18,


  gender:'男' 


}


  


声明函数类型   

let count: (a: number, b: number) => number



count = function (x, y) {


  return x + y 


}


备注:

· TypeScript 中的=>在函数类型声明时表示函数类型,描述其参数类型和返回类型。

· JavaScript 中的=>是一种定义函数的语法,是具体的函数实现。

· 函数类型声明还可以使用:接口、自定义类型等方式,下文中会详细讲解。


声明数组类型   

let arr1: string[] 

let arr2: Array<string>


arr1 = ['a','b','c']

arr2 = ['hello','world']


备注:上述代码中的Array<string>属于泛型,下文会详细讲解。



6. tuple

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


// 第一个元素必须是 string 类型,第二个元素必须是 number 类型。

let arr1: [string,number]

// 第一个元素必须是 number 类型,第二个元素是可选的,如果存在,必须是 boolean 类型。

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声明时是两个元素,赋值的是三个


arr1 = ['hello',123,false]



7. enum

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

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

function walk(str:string) {


  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')



 1. 数字枚举   

数字枚举一种最常见的枚举类型,其成员的值会自动递增,且数字枚举还具备反向映射的特点,在下面代码的打印中,不难发现:可以通过值来获取对应的枚举成员名称 。


// 定义一个描述【上下左右】方向的枚举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


使用数字枚举完成刚才walk函数中的逻辑,此时我们发现: 代码更加直观易读,而且类型安全,同时也更易于维护。


enum Direction {

  Up,

  Down,

  Left,

  Right,

}



function walk(n: Direction) {


  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)


 

2. 字符串枚举   

 枚举成员的值是字符串 


enum Direction {

  Up = "up",

  Down = "down",

  Left = "left",

  Right = "right"

}



let dir: Direction = Direction.Up;


console.log(dir); // 输出: "up"


 3. 常量枚举   


官方描述:常量枚举是一种特殊枚举类型,它使用const关键字定义,在编译时会被内联,避免生成一些额外的代码。 

何为编译时内联?

所谓“内联”其实就是 TypeScript 在编译时,会将枚举成员引用替换为它们的实际值,而不是生成额外的枚举对象。这可以减少生成的 JavaScript 代码量,并提高运行时性能。 

 

使用普通枚举的 TypeScript 代码如下:

enum Directions {

  Up,

  Down,

  Left,

  Right

}



let x = Directions.Up;


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


"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;


使用常量枚举的 TypeScript 代码如下:


const enum Directions {

  Up,

  Down,

  Left,

  Right

}


let x = Directions.Up;


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


"use strict";


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



8. type

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


1. 基本用法   

类型别名使用type关键字定义,type后跟类型名称,例如下面代码中num是类型别名。


type num = number;


let price: num


price = 100


 2. 联合类型    

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


type Status = number | string


type Gender = '男' | '女'



function printStatus(status: Status) {


  console.log(status);


}



function logGender(str:Gender){


  console.log(str)


}



printStatus(404);


printStatus('200');


printStatus('501');



logGender('男')


logGender('女')


3.交叉类型   

 交叉类型(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: '702'


};



9. 一个特殊情况


先来观察如下两段代码:


代码段1(正常)   


在函数定义时,限制函数返回值为void,那么函数的返回值就必须是空。


function demo():void{


  // 返回undefined合法


  return undefined



  // 以下返回均不合法


  return 100


  return false


  return null


  return []


}


demo()


代码段2(特殊)   


使用类型声明限制函数返回值为void时,TypeScript并不会严格要求函数返回空。


type LogFunc = () => void



const f1: LogFunc = () => {


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


};



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



const f3: LogFunc = function () {


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


};



为什么会这样?   


是为了确保如下代码成立,我们知道 Array.prototype.push 的返回值是一个数字,而Array.prototype.forEach方法期望其回调的返回类型是void。


const src = [1, 2, 3];


const dst = [0];


 


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


本文地址:https://www.jinpeng.work/?id=224
若非特殊说明,文章均属本站原创,转载请注明原链接。
广告3

欢迎 发表评论:

  • 请填写验证码

日历

«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930

控制面板

您好,欢迎到访网站!
  查看权限
广告2

退出请按Esc键