TypeScript知识点
在不运行代码的情况下检测代码中的错误,称为静态检查。根据要操作的值的类型,来确定是什么错误和什么不是错误,称为静态类型检查。
基本概念
TypeScript 是静态类型检查器。
- TS是JS的超集,因此的任何可执行的JS语句都是合法的TS。
- TS可以通过 TypeScript 编译器或 Babel 转译为原生JS代码。
- TS不会改变JS代码的runtime行为/表现,这个是TS的一个foundational promise。
有两种语法构建TS的类型:interface 和 type。建议使用前者,只在特殊类型使用后者(如:由简单类型组合创建复杂的类型)。
同名的 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
unknown 与 any 类似,同样表示允许接收任意类型。但比 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