# 原始类型-JavaScript 原始类型

本节聚焦 TypScript 基于 JavaScript 定义的原始类型

# 值和类型

参考 MDN 值 (opens new window) 和 类型 (opens new window) 的定义:

  • 值 环境存储的实际数据,对于 JS 可能是原始值或对象
  • 类型 具有特定特征的值,决定值的操作属性

在 TypeScript 中类型是一个抽象概念,表示一系列值的集合,确定了值的操作属性,而值是会在运行时真实存在的数据,理解上述概念是正确认识 TypeScript 类型系统的关键。 你需要区分 原始值 和 原始类型。 例如变量中存储的 1,NaN,Infinity 等我们称之为值。 而符合 ECMAScript 规范的所有这些值的集合为 Number 类型,由于是 Number 类型 所以这些值可以进行乘,除操作。注意不同语言对于值的操作性可能存在区别,这里只讨论 JavaScript。

JavaScript 中的变量是没有类型的,只有值才有。变量可以随时持有任何类型的值。 出自 《你不知道的 JavaScript 中卷 1.3》

正是由于 JavaScript 的变量没有类型,若变量由于赋值变化,导致类型变更,而任然进行原先值才具有的操作就发生了如下错误:

let a= 2;
let doubleA =a*2; // 返回 4

// 意外操作导致 a 变为字符串
a='foo';

// 由于字符串无法进行乘法操作
doubleA=a*2; // 返回 NaN

TypeScript 类型系统的核心是限制变量值的范围及操作来避免弱类型导致的问题!

在后续阅读中请注意区分 值 和 类型 的差异!

# 原始值类型

参考 MDN 原始类型 (opens new window),目前 JS 包含如下原始类型:

  • null
  • undefined
  • Boolean
  • Number
  • String
  • Symbol (ES6 新增)
  • BigInt (ES2020 新增 )

TypeScript 吸收了 JavaScript 的原始类型,采用小写表示上述类型。

  • null
  • undefined
  • boolean
  • number
  • string
  • symbol
  • bigint

各类型的限制参见示例

注意 bigint 特性在 TypeScript 3.2 (opens new window) 中加入,修改tsconfig.json 中 target (opens new window) 字段配置为 ESNEXT 即可生效。

target 决定了 TypeScript 如何编译 ts 文件。例如设置 target: "ES5",则 .ts 文件中的 ES6 箭头函数会被编译为功能相同的函数表达式以兼容 ES5。

# 特殊符号 (unique symbol)

采用 unique symbol 创建一个特殊符号类型。

// unique symbol 必须为常量,且值必须为 symbol
const uniqueFoo: unique symbol = Symbol();

详细资料参阅 unique symbol (opens new window)

# 原始封装对象类型

JavaScript 的原始类型的值上也可以调用方法,这是因为基础值在 JS 引擎内部会装换为 原始封装对象 (opens new window)。除去 null,undefined 其他类型均存在原始封装对象。

  • String
  • Number
  • BigInt
  • Boolean
  • Symbol

TypeScript 采用上述符号表示原始封装对象类型。示例如下:

// Boolean 类型,值可以为 boolean 类型和 Boolean 的原始封装类型
let bool: Boolean = new Boolean(true);
bool.valueOf();
bool = false;
bool = true;

// Number 类型,值可以为 number 类型和 Number 的原始封装类型
let numVal: Number = new Number(1); // 十进制
numVal.toString(16); // 转转为 16 进制
numVal = 1; // 也支持数值

//String 类型,,值可以为 string 类型和 String 的原始封装类型
let strVal: String = new String('foo');
strVal.split(''); // 拆分字符串为单个字符
strVal = 'foo'; // 支持赋值为字符串

//Symbol 类型, 值可以为 symbol 类型和 Symbol 的原始封装类型
let symVal: Symbol = Object(Symbol('foo')); // 注意 Symbol 不能采用 new 只可采用 Object 创建原始封装类型
symVal.valueOf();
sym = Symbol.for('bar'); // 创建一个全局符号

//BigInt, 值可以为 string 类型和 BigInt 的原始封装类型
let bigVal: BigInt = Object(1n); // BigInt 也只能采用 Object 包裹的方式创建原始封装类型
bigVal.toString();
bigVal = 1n; // 支持 bigint 类型

let str1: string = '1';

TIP

原始封装类型实际上属于对象类型,由于和原始值类型相关放在此处一起讲解。

# 原始值类型和原始封装对象类型的区别

除 symbol,bigint 外,boolean,string,number 取值不能为原始封装对象

// boolean 类型不接受原始封装类型的值
let boolVal: boolean = new Boolean(true); // 检查报错

// number 类型不接受原始封装类型的值
let num: number = new Number(1); // 检查报错

// string 类型不接受原始封装类型的值
let str: string = new String('foo'); // 检查报错

// symbol 可接受原始封装类型的值
let sym1: symbol = Object(Symbol('foo')); // 合法

// bigint 可接受原始封装类型的值
let bigIntVal: bigint = Object(1n); // 合法

由于在 JavaScript 我们多采用字面量形式定义原始类型,而不会使用构造器,所以请只采用小写的原始值类型限定对应变量。TS 官方文档 (opens new window) 也给出了此建议。

TIP

TypeScript 将 JavaScript 中的原始类型细分为原始值类型和原始封装对象类型,是为了通过更精细的类型限制避免 JavaScript 弱类型的缺陷。随着后续类型系统逐渐扩充,你会有更深刻的体会。

# 总结

本小节我们了解了 TypeScript 参考 JavaScript 定义的基础类型。知识点如下

  1. 理解了值和类型的区别
    1. 类型是一系列值的集合,确定了值的操作属性(注意是类型限定了值的操作属性,注意主次关系)
    2. TypeScript 类型系统的核心是限制变量值的范围及操作来避免弱类型导致的问题!
  2. 原始值类型
    • null
    • undefined
    • boolean
    • number
    • string
    • symbol
    • bigint 必须配置 target:"ESNEXT" 才有效
  3. 原始封装类型,注意为对象类型
    • String
    • Number
    • BigInt
    • Boolean
    • Symbol
  4. 原始值类型不能赋值为原始封装对象,一般只采用原始值类型。