TypeScript知识点


官网get started

在不运行代码的情况下检测代码中的错误,称为静态检查。根据要操作的值的类型,来确定是什么错误和什么不是错误,称为静态类型检查

基本概念

TypeScript 是静态类型检查器。

  • TS是JS的超集,因此的任何可执行的JS语句都是合法的TS。
  • TS可以通过 TypeScript 编译器或 Babel 转译为原生JS代码。
  • TS不会改变JS代码的runtime行为/表现,这个是TS的一个foundational promise。

有两种语法构建TS的类型:interfacetype。建议使用前者,只在特殊类型使用后者(如:由简单类型组合创建复杂的类型)。

同名的 interface 声明会合并,而 type 不允许重名。

用法

通过 interface 明确表述一个对象的形状。之后可以在变量声明、函数返回值、函数入参使用它。

interface User {
  name: string;
  id: number;
}
const user: User = {
  name: "Hayes",
  id: 0,
};
function getAdminUser(): User {
  //...
}
function deleteUser(user: User) {
  // ...
}

使用 typeof 检查变量的类型。

typeof s === "string" // 其余基本类型返回 "number"、"bigint"、"boolean"、"symbol"、"object"
typeof undefined === "undefined"
typeof f === "function"
Array.isArray(a) // 特例

Unions

js 没有内置的枚举类型,通过使用 unions 符号,枚举基本类型 string 或 number 常量描述允许的值,生成自定义类型。

type LockStates = "locked" | "unlocked";
type OddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;

Generics

没有 generics 的数组可以保护任何类型的值,拥有 generics 的数组描述了数组项的类型。

也可以使用 generics 定义自己的类型。

type StringArray = Array<string>;
type ObjectWithNameArray = Array<{ name: string }>;

Structural Type System

TS的核心概念之一是类型检查聚焦在values的shapes/结构。在结构体类型系统里:

  • 如果两个对象拥有相同的 shape,则认为它们是相同的 type
  • shape-matching 结构匹配只需要对象 field 的子集匹配
  • 只需要拥有 interface 要求的 shape,无需明确的写 implements,就可以实现一个接口 implement an interface
interface Point {
  x: number;
  y: number;
}
function printPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}
const point = { x: 12, y: 26 };
printPoint(point); // prints "12, 26"

const rect = { x: 33, y: 3, width: 30, height: 80 };
printPoint(rect); // prints "33, 3"

// classes 和 objects 遵从类型的方式没有区别
class VirtualPoint {
  x: number;
  y: number;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}
const newVPoint = new VirtualPoint(13, 56);
printPoint(newVPoint); // prints "13, 56"

只读

在 JS 中变量值是可变的。TS 通过 readonly 标志符可以指定对象某个或全部属性为只读。

interface Rx {
  readonly x: number;
}
let rx: Rx = { x: 1 };
rx.x = 12; // Cannot assign to 'x' because it is a read-only property.

interface X {
  x: number;
  y: string;
}
let rx: Readonly<X> = { x: 2, y: 'hello' };
rx.x = 13;      // Cannot assign to 'x' because it is a read-only property
rx.y = 'world'; // Cannot assign to 'y' because it is a read-only property

Basic Type

Enum

这个类型很有意思,可以给数字型值提供友好的名称。

枚举类型值默认从 0 开始,可以手动设置其成员所代表的数值。

enum Color {
  Red = 1,
  Green,
  Blue = 4,
}
let c: Color = Color.Green;       // 2
let colorName: string = Color[c]; // "Green"
console.log(Color[4]);            // "Blue"

Any

当变量的类型信息不明确(JS中很多API允许接收任意类型的参数,当TS与已有的js代码一起协作时),使用 any 类型可以让变量免除类型检查

该类型允许变量访问任意属性,甚至是不存在的属性。

any 类型会穿透对象的属性。

使用 any 会丢失掉使用TS的主要动机——类型安全,非必要时应尽量避免。

Unknown

unknownany 类似,同样表示允许接收任意类型。但比 any 更安全。

无法对 unknown 的变量使用任何其他明确类型的方法,除 Object 的 valueOf()toString()

举例:

let value_unknown: unknown;
// 以下ts会自动报错,因为当前没有明确变量是什么类型。
value_unknown.foo.bar; // Error: Object is of type 'unknown'.
value_unknown(); // Error: Object is of type 'unknown'.

// 在对unknown类型的变量进行类型检查之前,不能进行任何操作。要这样做:
if (typeof value_unknown === "function") {
  value_unknown();
}

let value_any: any;
// 以下TS不会报错,但最终运行时会报错。因此,any虽然使用灵活,但存在安全隐患。
value_any.foo.bar; // OK
value_any(); // OK

set new property on window object

需要扩展现有的 Window interface,告诉它自定义的新属性。参考:https://stackoverflow.com/a/12709880/2474841

declare global {
  interface Window {
    ttq: any;
  }
}

强类型 function 作为函数parameter

参考:https://stackoverflow.com/questions/14638990/are-strongly-typed-functions-as-parameters-possible-in-typescript

class Foo {
  save(callback: (n: number) => any) : void {
    callback(42);
  }
}

// Equivalent
type NumberCallback = (n: number) => any;
class Foo {
  save(callback: NumberCallback) : void {
    callback(42);
  }
}

value类型确定而key名不确定的 object

type ordinaryObject = {[key: string]: number};

运行 .ts文件

全局安装 tsc 编译器:

npm install -g typescript

编译:

tsc hello.ts