返回首页

说说 TypeScript 的数据类型有哪些?

问题解析

本题考察对 TypeScript 类型系统的全面掌握,包括基础类型、特殊类型以及与 JavaScript 类型的对比。需要能够清晰区分各种类型的使用场景。

核心概念

  • 基础类型:boolean、number、string、array、tuple
  • 特殊类型:any、void、never、unknown
  • 对象类型:object、enum
  • 空类型:null、undefined

详细解答

1. 是什么

TypeScript 和 JavaScript 几乎一样,拥有相同的数据类型,另外在 JavaScript 基础上提供了更加实用的类型供开发使用。

在开发阶段,可以为明确的变量定义为某种类型,这样 TypeScript 就能在编译阶段进行类型检查,当类型不合符预期结果的时候则会出现错误提示。

2. 数据类型分类

TypeScript 的数据类型主要有如下:

类型 说明
boolean 布尔类型
number 数字类型
string 字符串类型
array 数组类型
tuple 元组类型
enum 枚举类型
any 任意类型
null / undefined 空值类型
void 无返回值类型
never 永不存在的值类型
object 对象类型

3. 各类型详解

3.1 boolean(布尔类型)

let flag: boolean = true;
flag = false; // 正确
// flag = 123; // 错误:不能将类型 "number" 分配给类型 "boolean"

3.2 number(数字类型)

数字类型,和 JavaScript 一样,TypeScript 的数值类型都是浮点数,可支持二进制、八进制、十进制和十六进制。

let num: number = 123;
// num = '456'; // 错误
num = 456; // 正确

// 进制表示
let decLiteral: number = 6;        // 十进制
let hexLiteral: number = 0xf00d;   // 十六进制
let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744;   // 八进制

3.3 string(字符串类型)

字符串类型,可以使用双引号(")或单引号(')表示字符串。

let str: string = 'this is ts';
str = 'test';

作为超集,当然也可以使用模板字符串进行包裹,通过 ${} 嵌入变量:

let name: string = 'Gene';
let age: number = 37;
let sentence: string = `Hello, my name is ${name}. I'll be ${age + 1} years old next month.`;

3.4 array(数组类型)

数组类型,跟 JavaScript 一致,通过 [] 进行包裹,有两种写法:

方式一:元素类型后面接上 []

let arr: string[] = ['12', '23'];
arr = ['45', '56'];

方式二:使用数组泛型 Array<元素类型>

let arr: Array<number> = [1, 2];
// arr = ['45', '56']; // 错误

3.5 tuple(元组类型)

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。

let tupleArr: [number, string, boolean];
tupleArr = [12, '34', true]; // ok
// tupleArr = [12, '34']; // 不 ok,缺少一个元素
// tupleArr = ['12', 34, true]; // 不 ok,类型位置不对

赋值的类型、位置、个数需要和定义(声明)的类型、位置、个数一致。

3.6 enum(枚举类型)

枚举类型是对 JavaScript 标准数据类型的一个补充,使用枚举类型可以为一组数值赋予友好的名字。

enum Color { Red, Green, Blue }
let c: Color = Color.Green;

3.7 any(任意类型)

可以指定任何类型的值,在编程阶段还不清楚类型的变量指定一个类型,不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查,这时候可以使用 any 类型。

let num: any = 123;
num = 'str';
num = true;

// 定义存储各种类型数据的数组
let arrayList: any[] = [1, false, 'fine'];
arrayList[1] = 100;

注意:滥用 any 会失去 TypeScript 的类型检查优势,应尽量避免。

3.8 null 和 undefined

在 JavaScript 中 null 表示"什么都没有",是一个只有一个值的特殊类型,表示一个空对象引用,而 undefined 表示一个没有设置值的变量。

默认情况下 nullundefined 是所有类型的子类型,就是说你可以把 nullundefined 赋值给 number 类型的变量。

let num: number | undefined;
console.log(num); // undefined
num = 123;
console.log(num); // 123

但是 TS 配置了 --strictNullChecks 标记,nullundefined 只能赋值给 void 和它们各自。

3.9 void(空值类型)

用于标识方法返回值的类型,表示该方法没有返回值。

function hello(): void {
  alert("Hello World");
}

// 变量声明为 void 只能赋值 undefined 或 null(非严格模式下)
let unusable: void = undefined;

3.10 never(永不存在的值类型)

never 是其他类型(包括 nullundefined)的子类型,可以赋值给任何类型,代表从不会出现的值。

但是没有类型是 never 的子类型,这意味着声明 never 的变量只能被 never 类型所赋值。

never 类型一般用来指定那些总是会抛出异常、无限循环的函数。

// 错误的写法
// let a: never;
// a = 123; // Error

// 正确的写法
let a: never = (() => {
  throw new Error('错误');
})();

// 返回 never 的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}

3.11 object(对象类型)

对象类型,非原始类型,常见的形式通过 {} 进行包裹。

let obj: object;
obj = { name: 'Wang', age: 25 };
obj = [1, 2, 3]; // 数组也是对象
obj = () => {};  // 函数也是对象

// 更精确的对象类型定义
let person: { name: string; age: number } = {
  name: "Tom",
  age: 25
};

4. 类型层次关系

        any
         |
    +----+----+
    |         |
  object    原始类型
    |       (string/number/boolean/symbol)
  +--+--+    |
  |     |    |
array  function null/undefined/void/never

深入理解

unknown 类型(TypeScript 3.0+)

unknown 是 TypeScript 3.0 引入的类型,是 any 的安全替代品。

let notSure: unknown = 4;
notSure = "maybe a string instead";
notSure = false;

// 与 any 的区别
let anyValue: any = 4;
anyValue.toFixed(); // 编译通过,运行可能报错

let unknownValue: unknown = 4;
// unknownValue.toFixed(); // 错误:对象类型为 "unknown"

// 使用 unknown 前必须进行类型检查
if (typeof unknownValue === 'number') {
  unknownValue.toFixed(); // 正确
}

类型收窄(Type Narrowing)

function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    // 此时 padding 被收窄为 number 类型
    return Array(padding + 1).join(" ") + value;
  }
  // 此时 padding 被收窄为 string 类型
  return padding + value;
}

最佳实践

  1. 优先使用具体类型:避免使用 any,使用 unknown 作为不确定类型的临时方案
  2. 数组类型选择
    • 简单数组:number[]Array<number>
    • 固定长度、固定类型位置:使用元组 tuple
  3. 返回值类型
    • 明确返回值:标注具体类型
    • 无返回值:使用 void
    • 抛出异常或死循环:使用 never
  4. 严格空值检查:开启 strictNullChecks,避免 null/undefined 引起的运行时错误

面试要点

  1. 基础类型:boolean、number、string、array、tuple
  2. 特殊类型
    • any:任意类型,慎用
    • unknown:安全的 any(TS 3.0+)
    • void:无返回值
    • never:永不存在的值
  3. enum 枚举:数字枚举、字符串枚举、异构枚举
  4. null 和 undefined:默认是所有类型的子类型,严格模式下受限
  5. 类型层次:理解 never 是最底层(所有类型的子类型),any 是顶层(所有类型的父类型)