《TypeScript全面指南》第四章:TypeScript 的类型系统

103 阅读6分钟

TypeScript 的类型系统是 JavaScript 的超集,它为开发者提供了强大的类型工具以便更好地描述程序的行为。

基本类型

boolean 类型

像JavaScript, TypeScript也有**boolean**类型用来表示真/假值。

let isDone: boolean = false;

string 类型

用来表示文本字符串数据。

let name: string = "ChatGPT";

number 类型

用于表示整数浮点数

let age: number = 4;

bigint 类型

bigint是一个用于表示任意大整数的类型。

let big: bigint = 100n;

symbol 类型

**symbol**类型是ECMAScript 2015引入的原始数据类型,表示唯一的值。

const uniqueSymbol: symbol = Symbol();

object 类型

在TypeScript中,**object**类型表示非原始类型。

let obj: object = { key: "value" };

undefined 类型,null 类型

undefinednull 在 TypeScript 中也是它们自己的类型。它们与其他所有类型组合在一起形成特定的联合类型。

let u: undefined = undefined;
let n: null = null;

包装对象类型

包装对象的概念

在 JavaScript 中,字符串、数字、布尔值都有相应的包装对象,例如 String, Number, 和 Boolean。它们提供了一种将原始值视为对象的方式。

包装对象类型与字面量类型

这些包装对象的类型和字面量类型是不同的。例如:

let str: string = "hello";
let strObj: String = new String("hello");

Object 类型与 object 类型

Object 类型

Object 类型表示所有的JavaScript对象。它不是非常有用,因为几乎所有的值都是对象。

let obj: Object = { name: "ChatGPT" };

object 类型

Object 类型相比,object 类型是一种更严格和有用的类型。它表示值是一个对象,并且不是 number, string, boolean, bigint, symbol, null, 或 undefined

let o: object = { key: "value" };

Object 和 object 的区别

在 TypeScript 中,Objectobject 是两种不同的类型,它们在某些方面有所区别。以下是关于它们的详细对比:

1. Object 类型:

  • Object 代表 JavaScript 的全局对象类型。它对应于 JavaScript 中的所有非原始值。
  • 这意味着几乎任何值都可以被赋给一个 Object 类型的变量,包括函数、数组和普通的对象。
  • 使用 Object 类型会导致 TypeScript 的类型检查器放宽对成员属性和方法的检查,因为它基本上可以代表任何东西。

2. object 类型:

  • object 是 TypeScript 引入的一种类型,代表所有非原始类型的值。
  • 它比 Object 更严格,因为它不包括原始类型,如 number, string, boolean, bigint, symbol, undefinednull
  • 当你试图访问一个 object 类型值的属性或方法时,TypeScript 通常会发出错误,除非你更明确地指定了该对象的结构或使用类型断言。

示例:

function getObject(obj: Object) {
    // This is allowed, because `Object` is very permissive
    console.log(obj.toString());
}

function getobject(obj: object) {
    // This would result in a type error
    // console.log(obj.toString());

    // You'd need to do a type assertion or narrow down the type
    console.log((obj as any).toString());
}

总结:

  • Object 类型更为宽泛,几乎包括了所有的值。
  • object 类型更为严格,只包括非原始类型的值,通常需要更明确的类型信息或类型断言来访问其属性或方法。

在大多数情况下,当你希望类型表示一个对象,并且不关心它的具体结构时,使用 object 类型可能更为适合,因为它提供了更强的类型安全性。而当你确实需要一个可以代表任何东西的类型时,Object 类型可能更有用。

undefined 和 null 的特殊性

在 TypeScript 中,undefinednull 实际上是所有其他类型的子类型。这意味着你可以将 undefinednull 赋值给 number 类型或其他类型的变量,除非你使用了 strictNullChecks 选项。

例如,当 strictNullChecks 被开启时,以下代码将会导致编译时错误:

let age: number;
age = undefined;  // Error when strictNullChecks is enabled

值类型

TypeScript 还支持枚举 (enum) , 数组 (Array<T>**T[]**) 和元组 (tuple) 等更复杂的类型。

联合类型

联合类型表示一个值可以是几种类型之一。例如:

let union: number | string = 5;
union = "hello";

交叉类型

交叉类型表示一个值是多个类型的组合。例如:

type First = { a: number };
type Second = { b: string };

let intersection: First & Second = { a: 1, b: "hello" };
  • 使用 & 符号来创建一个交叉类型,First & Second。这意味着任何被认为是这种交叉类型的对象都必须满足 FirstSecond 这两种类型的约束。
  • 在这里,变量 intersection 的类型是 First & Second,这意味着它必须有一个 a 属性(其类型是 number)和一个 b 属性(其类型是 string)。
  • 我们为变量 intersection 分配了一个对象,该对象符合上述两种类型的要求,因此这个赋值是有效的。

结果

交叉类型允许我们将 FirstSecond 这两种类型合并到一起,创建一个同时有 ab 这两个属性的新类型。

总结

交叉类型是 TypeScript 的一个强大特性,它提供了一种简单而直接的方式来组合多个类型。这尤其在某些设计模式、高阶函数或工具函数中非常有用,比如合并或混入对象属性时。

type 命令

TypeScript 中的 type 命令(或称为 type 别名)允许我们给一个类型定义一个新的名字。这不仅可以简化复杂类型的表示,而且还提供了一种组织和重用类型定义的方式。

type CustomType = number | string;

typeof 运算符

在TypeScript中,**typeof**运算符可以用于获取变量的类型。

let x = 5;
type XType = typeof x;  // number

块级类型声明

TypeScript 允许在特定的作用域内声明类型,而这些类型在外部是不可见的。

类型的兼容

TypeScript 的类型系统是基于结构子类型的。如果一个类型 Y 可以被赋值给另一个类型 X,那么 Y 就被认为是 X 的子类型。

例如:

interface Named {
    name: string;
}

let x: Named;
let y = { name: "Alice", location: "Wonderland" };
x = y;  // OK, because y matches structure of Named.
  1. Named 接口:定义了一个属性 name,其类型是 string
  2. y 对象:有两个属性 - namelocation。尽管 yNamed 中没有定义的 location 属性,但它仍然有一个 name 属性,并且该属性的类型为 string
  3. 赋值:当我们尝试将 y 赋给 x 时,TypeScript 会检查 y 是否至少具有 Named 中的所有属性。由于 y 的结构匹配 Named 的要求(至少包含一个名为 namestring 类型的属性),所以这个赋值是有效的。

结论:

这种基于结构的类型兼容性在实际的开发中非常有用,因为它允许更加灵活的代码组织和重构。例如,你可以很容易地扩展或修改对象的结构,只要它满足某个接口的最小要求,你的代码就不会产生错误。

然而,这也意味着开发者需要更加小心,因为即使你不打算使一个对象兼容另一个类型,只要结构匹配,TypeScript 也会认为它们是兼容的。这在某些情况下可能会导致意外的行为,所以开发者需要确保他们的意图明确。