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 类型
undefined 和 null 在 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 中,Object 和 object 是两种不同的类型,它们在某些方面有所区别。以下是关于它们的详细对比:
1. Object 类型:
Object代表 JavaScript 的全局对象类型。它对应于 JavaScript 中的所有非原始值。- 这意味着几乎任何值都可以被赋给一个
Object类型的变量,包括函数、数组和普通的对象。 - 使用
Object类型会导致 TypeScript 的类型检查器放宽对成员属性和方法的检查,因为它基本上可以代表任何东西。
2. object 类型:
object是 TypeScript 引入的一种类型,代表所有非原始类型的值。- 它比
Object更严格,因为它不包括原始类型,如number,string,boolean,bigint,symbol,undefined和null。 - 当你试图访问一个
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 中,undefined 和 null 实际上是所有其他类型的子类型。这意味着你可以将 undefined 或 null 赋值给 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。这意味着任何被认为是这种交叉类型的对象都必须满足First和Second这两种类型的约束。 - 在这里,变量
intersection的类型是First & Second,这意味着它必须有一个a属性(其类型是number)和一个b属性(其类型是string)。 - 我们为变量
intersection分配了一个对象,该对象符合上述两种类型的要求,因此这个赋值是有效的。
结果
交叉类型允许我们将 First 和 Second 这两种类型合并到一起,创建一个同时有 a 和 b 这两个属性的新类型。
总结
交叉类型是 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.
Named接口:定义了一个属性name,其类型是string。y对象:有两个属性 -name和location。尽管y有Named中没有定义的location属性,但它仍然有一个name属性,并且该属性的类型为string。- 赋值:当我们尝试将
y赋给x时,TypeScript 会检查y是否至少具有Named中的所有属性。由于y的结构匹配Named的要求(至少包含一个名为name的string类型的属性),所以这个赋值是有效的。
结论:
这种基于结构的类型兼容性在实际的开发中非常有用,因为它允许更加灵活的代码组织和重构。例如,你可以很容易地扩展或修改对象的结构,只要它满足某个接口的最小要求,你的代码就不会产生错误。
然而,这也意味着开发者需要更加小心,因为即使你不打算使一个对象兼容另一个类型,只要结构匹配,TypeScript 也会认为它们是兼容的。这在某些情况下可能会导致意外的行为,所以开发者需要确保他们的意图明确。