TypeScript中的基本类型 | 8月更文挑战

190 阅读6分钟

以下内容是本人在学习拉勾教育专栏《TypeScript入门实战笔记》和个人的一些实践总结

在语法层面,缺省类型注解的 TypeScript 与 JavaScript 完全一致。因此,我们可以把 TypeScript 代码的编写看作是为 JavaScript 代码添加类型注解 在 JavaScript 中,原始类型指的是非对象且没有方法的数据类型,它包括 string、number、bigint、boolean、undefined 和 symbol 这六种 (null 是一个伪原始类型,它在 JavaScript 中实际上是一个对象,且所有的结构化类型都是通过 null 原型链派生而来)。原始类型值是最底层的实现,对应到 TypeScript 中同样也是最底层的类型

字符串

在 JavaScript 中,我们可以使用string表示 JavaScript 中任意的字符串(包括模板字符串),具体示例如下所示:

let firstname: string = 'Forest'; // 字符串字面量
let familyname: string = String('C'); // 显式类型转换
let fullname: string = `my name is ${firstname}.${familyname}`; // 模板字符串

所有 JavaScript 支持的定义字符串的方法,我们都可以直接在 TypeScript 中使用

数字

同样,我们可以使用number类型表示 JavaScript 已经支持或者即将支持的十进制整数、浮点数,以及二进制数、八进制数、十六进制数,具体的示例如下所示:

/** 十进制整数 */
let integer: number = 6;
​
/** 十进制整数 */
let integer2: number = Number(42);
​
/** 十进制浮点数 */
let decimal: number = 3.14;
​
/** 二进制整数 */
let binary: number = 0b1010;
​
/** 八进制整数 */
let octal: number = 0o744;
​
/** 十六进制整数 */
let hex: number = 0xf00d;

如果使用较少的大整数,那么我们可以使用bigint类型来表示

let big: bigint =  100n;

::: warning

虽然number和bigint都表示数字,但是这两个类型不兼容。

:::

布尔值

使用boolean表示 true 或者 false,该值可以比较(==、===、||、&&)、可以否定(!)

/** TypeScript 真香 为 真 */
let typeScriptIsGreat: boolean = true;
​
 /** TypeScript 太糟糕了 为 否 */
let typeScriptIsBad: boolean = false;

symbol

自 ECMAScript 6 起,TypeScript 开始支持新的Symbol原始类型, 即我们可以通过Symbol构造函数,创建一个独一无二的标记;同时,还可以使用symbol表示如下代码所示的类型

let sym1: symbol = Symbol();
let sym2: symbol = Symbol('42');

TypeScript 还包含 Number、String、Boolean、Symbol 等类型(注意区分大小写);当为大写时表示类型字面量

any

any指的是一个任意类型,是类型中的教父。为了目的它不惜一切代价,但是不要轻易请它纯棉,除非不得已;在TypeScript中,编译时一定都要有类型,如果你无法确定类型是什么,默认为any;这是个兜底类型,应该尽可能的避免使用;any类型的值就像常规的JavaScript一样,我们可以对被注解为 any 类型的变量进行任何操作,包括获取事实上并不存在的属性、方法,并且 TypeScript 还无法检测其属性是否存在、类型是否正确。类型检查器完全发挥不了作用

let anything: any = {};
anything.doAnything(); // 不会提示错误
anything = 1; // 不会提示错误
anything = 'x'; // 不会提示错误
​
let num: number = anything; // 不会提示错误
let str: string = anything; // 不会提示错误

any 类型会在对象的调用链中进行传导,即所有 any 类型的任意属性的类型都是 any

let anything: any = {};
let z = anything.x.y.z; // z 类型是 any,不会提示错误
z(); // 不会提示错误

unknown

unknown类型,它主要用来描述类型并不确定的变量;unknown类型的值可以比较、可以否定、可以使用JavaScript的typeof和instanceof运算符细化

比如在多个 if else 条件分支场景下,它可以用来接收不同条件下类型各异的返回值的临时变量,如下代码所示:

let result: unknown;
if (x) {
  result = x();
} else if (y) {
  result = y();
} ...

与 any 不同的是,unknown 在类型上更安全。比如我们可以将任意类型的值赋值给 unknown,但 unknown 类型的值只能赋值给 unknown 或 any,如下代码所示:

let result: unknown; 
let num: number = result; // 提示 ts(2322) 
let anything: any = result; // 不会提示错误

使用 unknown 后,TypeScript 会对它做类型检测。但是,如果不缩小类型(Type Narrowing),我们对 unknown 执行的任何操作都会出现如下所示错误:

let result: unknown; 
result.toFixed(); // 提示 ts(2571) 

而所有的类型缩小手段对 unknown 都有效,如下代码所示:

let result: unknown; 
if (typeof result === 'number') { 
 result.toFixed(); // 此处 hover result 提示类型是 number,不会提示错误
 } 

void、undefined、null

void 类型,它仅适用于表示没有返回值的函数。即如果该函数没有返回值,那它的类型就是 void。在 strict 模式下,声明一个 void 类型的变量几乎没有任何实际用处,因为我们不能把 void 类型的变量值再赋值给除了 any 和 unkown 之外的任何类型变量。

undefined 类型 和 null 类型,它们是 TypeScript 值与类型关键字同名的唯二例外

let undeclared: undefined = undefined; 
let nullable: null = null; 

undefined 的最大价值主要体现在接口类型上,它表示一个可缺省、未定义的属性。

我们可以把 undefined 值或类型是 undefined 的变量赋值给 void 类型变量,反过来,类型是 void 但值是 undefined 的变量不能赋值给 undefined 类型。

const userInfo: { 
 id?: number; 
 } = {}; 
let undeclared: undefined = undefined; 
let unusable: void = undefined; 
unusable = undeclared; // ok
undeclared = unusable; // ts(2322) 

除此之外,undefined 和 null 类型还具备警示意义,它们可以提醒我们针对可能操作这两种(类型)值的情况做容错处理。也就是在操作之前判断值的类型是否支持当前的操作,类型守卫既能通过类型缩小影响 TypeScript 的类型检测,也能保障 JavaScript 运行时的安全性,如下代码所示:

const user: { 
 id?: number; 
 name?: null | string
 } = { id: 1, name: 'Captain' }; 
if (userInfo.id !== undefined) { 
 userInfo.id.toFixed(); // id 的类型缩小成 number
 } 

我们可以使用非空断言来排除值可能为 null 或 undefined 的情况,但是这样很不安全。

userInfo.id!.toFixed(); 
userInfo.name!.toLowerCase() 

而比非空断言更安全、类型守卫更方便的做法是使用单问号(Optional Chain)、双问号(空值合并),我们可以使用它们来保障代码的安全性,如下代码所示:

userInfo.id?.toFixed(); // Optional Chain
const myName = userInfo.name?? `my name is ${info.name}`; // 空值合并

never

never 表示永远不会发生值的类型

function ThrowError(msg: string): never { 
 throw Error(msg); 
 } 

以上函数因为永远不会有返回值,所以它的返回值类型就是 never。

never 是所有类型的子类型,它可以给所有类型赋值;但是反过来,除了 never 自身以外,其他类型(包括 any 在内的类型)都不能为 never 类型赋值。

let Unreachable: never = 1;       // 报错
Unreachable = 'string';           // 报错
Unreachable = true;               // 报错
let num: number = Unreachable;    // ok
let str: string = Unreachable;    // ok
let bool: boolean = Unreachable;  // ok