关于TypeScript

对 TypeScript 语法&特性的理解 CheatSheet

主要参考: https://www.tslang.cn/docs/handbook/

Basic Variable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let isDone: boolean = false;

let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;

let name: string = "bob";
name = "smith";


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


let sentence: string = "Hello, my name is " + name + " " +
"I'll be " + (age + 1) + " years old next month.";

Array

1
2
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3]; // 数组泛型, Array<元素类型>

Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。

1
2
3
4
5
6
7
8
9
// Declare a tuple type
let x: [string, number];
// Initialize
x = ['hello', 10]; // OK
x = [10, 'hello']; // Error

console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'

当访问一个越界的元素,会使用联合类型替代

1
2
3
4
x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型
console.log(x[5].toString()); // OK, 'string' 和 'number' 都有 toString

x[6] = true; // Error, 布尔不是(string | number)类型

Enum

使用枚举类型可以为一组数值赋予友好的名字, 默认情况下,从0开始为元素编号,可以手动的指定成员的数值。

可以由枚举的值得到它的名字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Color {Red, Green, Blue}
let c: Color = Color.Green;

enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;

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


enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

console.log(colorName); // 显示'Green'因为上面代码里它的值是2

Any 类型

在编程阶段还不清楚类型的变量指定一个类型。这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。可以使用 any类型来标记这些变量。

1
2
3
4
5
6
7
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

let list: any[] = [1, true, "free"];
list[1] = 100;

在对现有代码进行改写的时候,any类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。 你可能认为 Object有相似的作用,就像它在其它语言中那样。 但是 Object类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法:

1
2
3
4
5
6
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

Void 无返回值

void类型像是与any类型相反,它表示没有任何类型。当一个函数没有返回值时,通常其返回值类型是 void

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

声明一个void类型的变量没有什么大用,因为你只能为它赋予 undefinednull

1
let unusable: void = undefined

Null 和 Undefined

undefined和null两者各自有自己的类型分别叫做undefined和null。它们的本身的类型用处不是很大

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

默认情况下null和undefined是所有类型的子类型。 就是说你可以把 null和undefined赋值给number类型的变量。

多态(Polymorphism)

然而,当你指定了–strictNullChecks标记,null和undefined只能赋值给void和它们各自。 这能避免 很多常见的问题。 也许在某处你想传入一个 string或null或undefined,你可以使用联合类型string | null | undefined。

鼓励尽可能地使用–strictNullChecks

Never

never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。

never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}

Object 对象

object表示非原始类型,也就是除 number,string,boolean,symbol,null 或 undefined 之外的类型

1
2
3
4
5
6
7
8
9
declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

类型断言

有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。

通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。

尖括号 语法 / as 语法

1
2
3
4
5
6
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

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

Interface 接口

接口的作用就是为类型命名和为你的代码或第三方代码定义契约

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做 “鸭式辨型法” 或 “结构性子类型化”

1
2
3
4
5
6
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

类型检查器会查看printLabel的调用。 printLabel有一个参数,并要求这个对象参数有一个名为label类型为string的属性。 需要注意的是,我们传入的对象参数实际上会包含很多属性,但是编译器只会检查那些必需的属性是否存在,并且其类型是否匹配。

然而,有些时候 TypeScript 却并不会这么宽松,重写上面的例子,这次使用接口来描述:必须包含一个label属性且类型为string

1
2
3
4
5
6
7
8
9
10
interface LabelledValue {
label: string;
}

function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

LabelledValue接口就好比一个名字,用来描述上面例子里的要求。 它代表了有一个 label属性且类型为string的对象。 需要注意的是,我们在这里并不能像在其它语言里一样,说传给 printLabel的对象实现了这个接口。我们只会去关注值的外形。 只要传入的对象满足上面提到的必要条件,那么它就是被允许的。

还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。

可选属性

接口里的属性不全都是必需的。有些是只在某些条件下存在,或者根本不存在。可选属性在应用 “option bags” 模式时很常用,即给函数传入的参数对象中只有部分属性赋值了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface SquareConfig {
color?: string;
width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}

let mySquare = createSquare({color: "black"});

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。

可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。 比如,我们故意将 createSquare里的color属性名拼错,就会得到一个错误提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface SquareConfig {
color?: string;
width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
let newSquare = {color: "white", area: 100};
if (config.clor) {
// Error: Property 'clor' does not exist on type 'SquareConfig'
newSquare.color = config.clor;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}

let mySquare = createSquare({color: "black"});

Readonly

一些对象属性只能在对象刚刚创建的时候修改其值。你可以在属性名前用 readonly 来指定只读属性。通过赋值一个对象字面量来构造一个Point。 赋值后, x和y再也不能被改变了

1
2
3
4
5
6
7
interface Point {
readonly x: number;
readonly y: number;
}

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

TypeScript 具有ReadonlyArray类型,它与Array相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改

就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写

1
2
3
4
5
6
7
8
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

a = ro as number[];

最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用readonly

后续更新中…

作者

Jhuoer Yen

发布于

2025-06-03

更新于

2025-06-03

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×