为了使程序有用,我们需要能够处理一些最简单的数据单位:数字、字符串、结构、布尔值等。在TypeScript中,我们支持的类型与你在JavaScript中期望的类型相同,还有一个额外的枚举类型来帮助我们。

布尔

最基本的数据类型是简单的真/假值,JavaScript和TypeScript称之为布尔值。

  1. let isDone: boolean = false;

数字

在JavaScript中,TypeScript中的所有数字要么是浮点值,要么是BigIntegers。这些浮点数得到类型为number,而BigIntegers得到类型为bigint。除了十六进制和十进制字元,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字元。

  1. let decimal: number = 6;
  2. let hex: number = 0xf00d;
  3. let binary: number = 0b1010;
  4. let octal: number = 0o744;
  5. let big: bigint = 100n;

字符串

在JavaScript中为网页和服务器创建程序的另一个基本部分是处理文本数据。在其他语言中,我们使用字符串类型来引用这些文本数据类型。就像JavaScript一样,TypeScript也使用双引号(“)或单引号(‘)来包围字符串数据。

  1. let color: string = "blue";
  2. color = 'red';

您还可以使用模板字符串,它可以跨越多行,并具有嵌入式表达式。这些字符串由反引号/反引号 (`) 字符包围,嵌入式表达式的形式为 ${ expr }。

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

这就相当于这样声明句。

  1. let sentence: string =
  2. "Hello, my name is " +
  3. fullName +
  4. ".\n\n" +
  5. "I'll be " +
  6. (age + 1) +
  7. " years old next month.";

数组

TypeScript,像JavaScript一样,允许你使用数组的值。数组类型可以用两种方式之一来写。在第一种情况下,你使用元素的类型,然后用[]表示该元素类型的数组。

  1. let list: number[] = [1, 2, 3];

第二种方式使用通用数组类型Array

  1. let list: Array<number> = [1, 2, 3];

元组

元组类型允许你表达一个具有固定数量元素的数组,这些元素的类型是已知的,但不需要是相同的。例如,您可能希望将一个值表示为一对字符串和一个数字。

  1. // Declare a tuple type
  2. let x: [string, number];
  3. // Initialize it
  4. x = ["hello", 10]; // OK
  5. // Initialize it incorrectly
  6. x = [10, "hello"]; // Error
  7. // Type 'number' is not assignable to type 'string'.
  8. // Type 'string' is not assignable to type 'number'.

当访问一个具有已知索引的元素时,会检索到正确的类型。

  1. // OK
  2. console.log(x[0].substring(1));
  3. console.log(x[1].substring(1));
  4. // Property 'substring' does not exist on type 'number'.

访问已知索引集之外的元素会出错。

  1. x[3] = "world";
  2. // Tuple type '[string, number]' of length '2' has no element at index '3'.
  3. console.log(x[5].toString());
  4. // Object is possibly 'undefined'.
  5. // Tuple type '[string, number]' of length '2' has no element at index '5'.

枚举

在JavaScript的标准数据类型集中,一个有用的补充就是枚举。就像在C#语言中一样,枚举是一种为数值集赋予更友好名称的方式。

  1. enum Color {
  2. Red,
  3. Green,
  4. Blue,
  5. }
  6. let c: Color = Color.Green;

默认情况下,枚举从0开始编号,你可以通过手动设置其中一个成员的值来改变这一情况。例如,我们可以将前面的例子从1开始,而不是从0开始。

  1. enum Color {
  2. Red = 1,
  3. Green,
  4. Blue,
  5. }
  6. let c: Color = Color.Green;

或者,甚至可以手动设置枚举中的所有值。

  1. enum Color {
  2. Red = 1,
  3. Green = 2,
  4. Blue = 4,
  5. }
  6. let c: Color = Color.Green;

枚举的一个方便的特性是,你也可以从一个数值到枚举中该值的名称。例如,如果我们的值是2,但不确定它在上面的Color枚举中映射到什么,我们可以查找相应的名称。

  1. enum Color {
  2. Red = 1,
  3. Green,
  4. Blue,
  5. }
  6. let colorName: string = Color[2];
  7. // Displays 'Green'
  8. console.log(colorName);

未知

我们可能需要描述我们在编写应用程序时不知道的变量类型。这些值可能来自动态内容—例如来自用户—或者我们可能希望有意接受我们的API中的所有值。在这些情况下,我们希望提供一个类型,告诉编译器和未来的读者,这个变量可能是任何东西,所以我们给它未知类型。

  1. let notSure: unknown = 4;
  2. notSure = "maybe a string instead";
  3. // OK, definitely a boolean
  4. notSure = false;

如果你有一个未知类型的变量,你可以通过做typeof检查、比较检查或者更高级的类型防护来缩小它的范围,这些将在后面的章节中讨论。

  1. declare const maybe: unknown;
  2. // 'maybe' could be a string, object, boolean, undefined, or other types
  3. const aNumber: number = maybe;
  4. // Type 'unknown' is not assignable to type 'number'.
  5. if (maybe === true) {
  6. // TypeScript knows that maybe is a boolean now
  7. const aBoolean: boolean = maybe;
  8. // So, it cannot be a string
  9. const aString: string = maybe;
  10. // Type 'boolean' is not assignable to type 'string'.
  11. }
  12. if (typeof maybe === "string") {
  13. // TypeScript knows that maybe is a string
  14. const aString: string = maybe;
  15. // So, it cannot be a boolean
  16. const aBoolean: boolean = maybe;
  17. // Type 'string' is not assignable to type 'boolean'.
  18. }

任意

在某些情况下,并不是所有的类型信息都是可用的,或者它的声明会花费不适当的精力。这些情况可能发生在没有TypeScript或第三方库编写的代码中。在这些情况下,我们可能希望选择不进行类型检查。为了做到这一点,我们将这些值标记为任意类型。

  1. declare function getValue(key: string): any;
  2. // OK, return value of 'getValue' is not checked
  3. const str: string = getValue("myString");

Any类型是一种强大的方式来使用现有的JavaScript,允许你在编译过程中逐渐选择加入或退出类型检查。

与未知类型不同,any类型的变量允许你访问任意属性,甚至是那些不存在的属性。这些属性包括函数,TypeScript不会检查它们的存在或类型。

  1. let looselyTyped: any = 4;
  2. // OK, ifItExists might exist at runtime
  3. looselyTyped.ifItExists();
  4. // OK, toFixed exists (but the compiler doesn't check)
  5. looselyTyped.toFixed();
  6. let strictlyTyped: unknown = 4;
  7. strictlyTyped.toFixed();
  8. Object is of type 'unknown'.

任何将继续通过你的对象传播。

  1. let looselyTyped: any = {};
  2. let d = looselyTyped.a.b.c.d;
  3. // ^ = let d: any

毕竟,请记住,任何的便利都是以失去类型安全为代价的。类型安全是使用TypeScript的主要动机之一,在没有必要的时候,你应该尽量避免使用任何。

void有点像any的反义词:没有任何类型。你可能通常会把它看作是不返回值的函数的返回类型。

  1. function warnUser(): void {
  2. console.log("This is my warning message");
  3. }

声明void类型的变量是没有用的,因为你只能将null(只有在没有指定—strictNullChecks的情况下,见下一节)或undefined分配给它们。

  1. let unusable: void = undefined;
  2. // OK if `--strictNullChecks` is not given
  3. unusable = null;

Null and Undefined

在TypeScript中,undefined和null实际上都有自己的类型,分别命名为undefined和null。就像void一样,它们本身并不是非常有用。

  1. // Not much else we can assign to these variables!
  2. let u: undefined = undefined;
  3. let n: null = null;

默认情况下,null和undefined是所有其他类型的子类型。这意味着你可以将null和undefined赋值给像number这样的类型。

然而,当使用—strictNullChecks标志时,null和undefined只能分配给unknown、any和它们各自的类型(一个例外是undefined也可以分配给void)。这有助于避免许多常见的错误。在你想传入字符串或null或undefined的情况下,你可以使用联合类型string | null | undefined。

联合类型是一个高级话题,我们将在后面的章节中介绍。

需要注意的是:我们鼓励在可能的情况下使用 —strictNullChecks,但在本手册中,我们将假定它是关闭的。

never

never类型表示永远不会出现的值的类型。例如,never是一个函数表达式的返回类型,或者一个总是抛出异常的箭头函数表达式,或者一个永不返回的函数表达式。变量在被任何类型守卫缩小时,也会获得never类型,即永远不能为真。

never类型是每一个类型的子类型,并且可以分配给每一个类型;但是,没有任何类型是never的子类型,或者可以分配给never(除了never本身)。即使是 any 也不能分配给 never。

一些函数返回never的例子。

  1. // Function returning never must not have a reachable end point
  2. function error(message: string): never {
  3. throw new Error(message);
  4. }
  5. // Inferred return type is never
  6. function fail() {
  7. return error("Something failed");
  8. }
  9. // Function returning never must not have a reachable end point
  10. function infiniteLoop(): never {
  11. while (true) {}
  12. }

object

object是一种代表非原生类型的类型,即任何不是number、string、boolean、bigint、符号、null或undefined的类型。

有了对象类型,像Object.create这样的API就可以更好地表示。比如说

  1. declare function create(o: object | null): void;
  2. // OK
  3. create({ prop: 0 });
  4. create(null);
  5. create(undefined); // Remember, undefined is a subtype of null
  6. Argument of type 'undefined' is not assignable to parameter of type 'object | null'.
  7. create(42);
  8. Argument of type '42' is not assignable to parameter of type 'object | null'.
  9. create("string");
  10. Argument of type '"string"' is not assignable to parameter of type 'object | null'.
  11. create(false);
  12. Argument of type 'false' is not assignable to parameter of type 'object | null'.

一般来说,你不需要使用这个。

类型断言

有时候,你会在一个你比TypeScript更了解一个值的情况下结束。通常,当你知道某个实体的类型可能比它的当前类型更具体时,就会发生这种情况。

类型断言是一种告诉编译器 “相信我,我知道我在做什么 “的方式。类型断言就像其他语言中的类型转换一样,但它不会执行特殊的检查或数据重组。它对运行时没有影响,纯粹由编译器使用。TypeScript假设你,程序员,已经执行了任何你需要的特殊检查。

类型断言有两种形式。

一种是as-syntax。

  1. let someValue: unknown = "this is a string";
  2. let strLength: number = (someValue as string).length;

另一个版本是 “角括号 “语法。

  1. let someValue: unknown = "this is a string";
  2. let strLength: number = (<string>someValue).length;

这两个样本是等价的。使用一个而不是另一个主要是偏好的选择;然而,当使用TypeScript与JSX时,只允许使用as-style assertions。

关于让的说明
你可能已经注意到,到目前为止,我们一直在使用let关键字,而不是你可能更熟悉的JavaScript的var关键字。let关键字实际上是TypeScript提供的一个较新的JavaScript结构。你可以在Handbook Reference on Variable Declarations中阅读更多关于let和const如何解决var的很多问题。

关于Number、String、Boolean、Symbol和Object。
人们很容易认为Number、String、Boolean、Symbol或Object等类型与上面推荐的小写版本相同。然而,这些类型并不是指语言基元,几乎永远都不应该被用作类型。

  1. function reverse(s: String): String {
  2. return s.split("").reverse().join("");
  3. }
  4. reverse("hello world");

取而代之的是使用数字、字符串、布尔、对象和符号等类型。

  1. function reverse(s: string): string {
  2. return s.split("").reverse().join("");
  3. }
  4. reverse("hello world");