编辑
2023-05-29
前端
00
请注意,本文编写于 541 天前,最后修改于 538 天前,其中某些信息可能已经过时。

目录

基础数据类型标注
原始类型的类型标注
null 与 undefined
void
js 中的 void
ts 中的 void
数组的类型标注
数组类型 list
元组类型 tuple
数组和元组的只读标注 readonly
对象类型标注
interface 声明属性
可选属性 ?
只读属性 readonly
type 声明对象
装箱类型、 Object 、object 以及 { }
装箱类型(Boxed Types)
Object
其他装箱类型(Boxed Types)
非原始类型 object
非空类型 {}
Object 、object 以及 { } 的的使用场景

基础数据类型标注

原始类型的类型标注

JavaScript的内置原始类型,在 TypeScript 中它们都有对应的类型注解

ts
const name: string = 'linbudu'; const age: number = 24; const male: boolean = false; const undef: undefined = undefined; const nul: null = null; const obj: object = { name, age, male }; const bigintVar1: bigint = 9007199254740991n; const bigintVar2: bigint = BigInt(9007199254740991); const symbolVar: symbol = Symbol('unique');

null 与 undefined

JavaScript 中,

  • null: 这里没有值
  • undefined: 这里有值,但是个空值

而在 TypeScript 中,null 与 undefined 类型都是有具体意义的类型

  • 这两者在没有开启 strictNullChecks 检查的情况下,会被视作其他类型的子类型,比如 string 类型会被认为包含了 null 与 undefined 类型
ts
const tmp1: null = null; const tmp2: undefined = undefined; // 仅在关闭 strictNullChecks 时成立,下同 const tmp3: string = null; const tmp4: string = undefined;

void

js 中的 void

在 JavaScript 中,void 是一个运算符,可以将任何表达式的值转换为 undefined。使用 void 运算符时,只需要在其后面跟上要被计算的表达式即可。

js
void 0; // 返回 undefined void "hello"; // 返回 undefined void (1 + 2); // 返回 undefined

通常,void 运算符用于创建一个返回 undefined 的函数,以避免特定上下文中出现副作用。

js
void function iife() { console.log("Invoked!"); }();

ts 中的 void

TypeScript 与 JavaScript 中不同的是,这里的 void 用于描述

  • 一个内部没有 return 语句
  • 或者没有显式 return 一个值的函数的返回值
ts
// func1 与 func2 的返回值类型都会被隐式推导为 void // 但在实际的代码执行中,func1 与 func2 的返回值均是 undefined function func1() {} function func2() { return; } // func3 其返回值类型被推导为了 undefined function func3() { return undefined; }

相关信息

你可以认为 void 表示一个空类型,而 null 与 undefined 都是一个具有意义的实际类型

数组的类型标注

数组类型 list

TypeScript 中有两种方式来声明一个数组类型:

ts
// 这两种方式是完全等价的 const arr1: string[] = []; const arr2: Array<string> = [];

元组类型 tuple

数组是我们在日常开发大量使用的数据结构,但在某些情况下,使用 元组(Tuple) 来代替数组要更加妥当

ts
// 数组对越界访问不会报错 const arr3: string[] = ['lin', 'bu', 'du']; console.log(arr3[599]); // 元组是指定了长度的数组,在越界访问时会给出类型报错 const arr4: [string, string, string] = ['lin', 'bu', 'du']; console.log(arr4[599]); // 元组内部也可以声明多个与其位置强绑定的,不同类型的元素 const arr5: [string, number, boolean] = ['type', 599, true]; // 元组也支持了在某一个位置上的可选成员 const arr6: [string, number?, boolean?] = ['type']; // ts4.0后,可以为元组中的元素打上类似属性的标记 const arr7: [name: string, age: number, male?: boolean] = ['linbudu', 599, true];

元组的最大作用就是能够很好的提示显式或者隐式的越界访问

数组和元组的只读标注 readonly

数组与元组层面也有着只读的修饰

ts
const myArray: readonly number[] = [1, 2, 3]; const myArray: ReadonlyArray<number> = [1, 2, 3]; myArray[0] = 4; // 报错:Type 'readonly number[]' cannot be mutated. myArray.push(4); // 报错:Property 'push' does not exist on type 'readonly number[]'.
ts
const myTuple: readonly [string, number] = ["hello", 123]; myTuple[0] = "world"; // 报错:无法分配到 "myTuple" ,因为它是只读的。 myTuple.push(456); // 报错:属性 'push' 在只读数组上不存在。
  • 你只能将整个数组/元组标记为只读,而不能像对象那样标记某个属性为只读。

  • 一旦被标记为只读,那这个只读数组/元组的类型上,将不再具有 pushpop 等方法(即会修改原数组的方法),因此报错信息也将是类型 xxx 上不存在属性“push”这种。

  • 这一实现的本质是只读数组与只读元组的类型实际上变成了 ReadonlyArray,而不再是 Array。

对象类型标注

interface 声明属性

在 TypeScript 中我们也需要特殊的类型标注来描述对象类型,即 interface ,可以理解为它代表了这个对象对外提供的接口结构

ts
// 声明一个结构 interface IDescription { name: string; age: number; male: boolean; } // 使用这个结构来作为一个对象的类型标注 const obj1: IDescription = { name: 'linbudu', age: 599, male: true, };

interface的标注有以下限制作用:

  • 每一个属性的值必须一一对应到接口的属性类型
  • 不能有多的属性,也不能有少的属性,包括直接在对象内部声明,或是 obj1.other = 'xxx' 这样属性访问赋值的形式

可选属性 ?

类似于元组可选,在接口结构中同样通过 ? 来标记一个属性为可选

ts
interface IDescription { name: string; age: number; male?: boolean; func?: Function; } // 即使你在 obj2 中定义了 male 属性,但当你访问 obj2.male 时 // 它的类型仍然会是 boolean | undefined const obj2: IDescription = { name: 'linbudu', age: 599, male: true, // 无需实现 func 也是合法的 };

只读属性 readonly

readonly可以标记这个属性为只读

ts
interface IDescription { readonly name: string; age: number; } const obj3: IDescription = { name: 'zhang san', age: 599, }; // 无法分配到 "name" ,因为它是只读属性 obj3.name = "silon";

type 声明对象

type(Type Alias,类型别名)也可以代替interface结构描述对象。

ts
type Person = { name: string; age: number; email?: string; // 可选属性 }; const person1: Person = { name: "Alice", age: 30, email: "alice@example.com" }; const person2: Person = { name: "Bob", age: 25 };

更推荐的方式是:

  • interface: 用来描述对象、类的结构
  • type:用来将一个函数签名、一组联合类型、一个工具类型等等抽离成一个完整独立的类型。

大部分场景下interface都可以被type所取代,因此,只要你觉得统一使用类型别名让你觉得更整齐,也没什么问题。

装箱类型、 Object 、object 以及 { }

装箱类型(Boxed Types)

Object

原型链的顶端是 Object 以及 Function,这也就意味着:

所有的原始类型与对象类型最终都指向 Object,在 TypeScript 中就表现为 Object 包含了所有的类型

ts
// 对于 undefined、null、void 0 ,需要关闭 strictNullChecks const tmp1: Object = undefined; const tmp2: Object = null; const tmp3: Object = void 0; const tmp4: Object = 'linbudu'; const tmp5: Object = 599; const tmp6: Object = { name: 'linbudu' }; const tmp7: Object = () => {}; const tmp8: Object = [];

其他装箱类型(Boxed Types)

Object 类似的还有 BooleanNumberStringSymbol,这几个装箱类型(Boxed Types) 同样包含了一些超出预期的类型。

String 为例,它同样包括 undefinednullvoid,以及代表的 拆箱类型(Unboxed Types) string,但并不包括其他装箱类型对应的拆箱类型,如 boolean 与 基本对象类型

const tmp9: String = undefined; const tmp10: String = null; const tmp11: String = void 0; const tmp12: String = 'linbudu'; // 以下不成立,因为不是字符串类型的拆箱类型 const tmp13: String = 599; // X const tmp14: String = { name: 'linbudu' }; // X const tmp15: String = () => {}; // X const tmp16: String = []; // X

在任何情况下,你都不应该使用这些装箱类型。

非原始类型 object

object 的引入就是为了解决对 Object 类型的错误使用

  • Object:代表所有类型
  • object:代表所有非原始类型的类型,即数组、对象与函数类型这些:
const tmp17: object = undefined; const tmp18: object = null; const tmp19: object = void 0; const tmp20: object = 'linbudu'; // X 不成立,值为原始类型 const tmp21: object = 599; // X 不成立,值为原始类型 const tmp22: object = { name: 'linbudu' }; const tmp23: object = () => {}; const tmp24: object = [];

非空类型 {}

{}表示一个合法的,但内部无属性定义的空对象。

它意味着任何非 null / undefined 的值

const tmp25: {} = undefined; // 仅在关闭 strictNullChecks 时成立,下同 const tmp26: {} = null; const tmp27: {} = void 0; // void 0 等价于 undefined const tmp28: {} = 'linbudu'; const tmp29: {} = 599; const tmp30: {} = { name: 'linbudu' }; const tmp31: {} = () => {}; const tmp32: {} = [];

虽然能够将其作为变量的类型,但你实际上无法对这个变量进行任何赋值操作

const tmp30: {} = { name: 'linbudu' }; tmp30.age = 18; // X 类型“{}”上不存在属性“age”。

Object 、object 以及 { } 的的使用场景

  • 在任何时候都不要,不要,不要使用 Object 以及类似的装箱类型

  • 当你不确定某个变量的具体类型,但能确定它不是原始类型,可以使用 object。

    • 推荐进一步区分如下:
    • 使用 Record<string, unknown> 或 Record<string, any> 表示对象
    • unknown[] 或 any[] 表示数组
    • (...args: any[]) => any表示函数
  • 要避免使用{}。{}意味着任何非 null / undefined 的值,从这个层面上看,使用它和使用 any 一样恶劣

本文作者:Silon汐冷

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!