ts学习

TypeScript 详细学习手册

第一部分:TypeScript 基础

1. TypeScript 简介

什么是 TypeScript?

  • JavaScript 的超集,添加了静态类型系统
  • 编译时类型检查,运行时仍然是纯 JavaScript
  • 由 Microsoft 开发和维护

优势:

  • 更好的代码可读性和维护性
  • 早期错误检测
  • 更好的 IDE 支持(智能提示、重构)
  • 渐进式采用

2. 开发环境搭建

安装 TypeScript

1
2
3
4
5
# 全局安装
npm install -g typescript

# 或者项目本地安装
npm install --save-dev typescript

创建第一个 TypeScript 文件

1
2
3
4
5
6
7
// hello.ts
function greet(name: string): string {
return `Hello, ${name}!`;
}

const message = greet("TypeScript");
console.log(message);

编译和运行

1
2
3
4
5
# 编译
tsc hello.ts

# 运行
node hello.js

tsconfig.json 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

3. 基础类型

原始类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 字符串
let name: string = "John";
let message: string = `Hello, ${name}!`;

// 数字
let age: number = 30;
let price: number = 19.99;
let binary: number = 0b1010;

// 布尔值
let isActive: boolean = true;
let isCompleted: boolean = false;

// null 和 undefined
let empty: null = null;
let notDefined: undefined = undefined;

// Symbol
let uniqueKey: symbol = Symbol("unique");

数组和元组

1
2
3
4
5
6
7
8
9
10
11
12
13
// 数组
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ["Alice", "Bob"];

// 只读数组
let readonlyNumbers: ReadonlyArray<number> = [1, 2, 3];

// 元组
let person: [string, number] = ["John", 30];
let rgb: [number, number, number] = [255, 0, 0];

// 解构元组
const [userName, userAge] = person;

任意类型和未知类型

1
2
3
4
5
6
7
8
9
10
11
12
// any - 关闭类型检查
let dynamicValue: any = "hello";
dynamicValue = 42;
dynamicValue = true;

// unknown - 类型安全的 any
let uncertainValue: unknown = "hello";
// uncertainValue.toUpperCase(); // 错误:必须先进行类型检查

if (typeof uncertainValue === "string") {
console.log(uncertainValue.toUpperCase()); // 正确
}

4. 函数

函数声明和表达式

1
2
3
4
5
6
7
8
9
10
11
12
// 函数声明
function add(x: number, y: number): number {
return x + y;
}

// 函数表达式
const multiply = function(x: number, y: number): number {
return x * y;
};

// 箭头函数
const divide = (x: number, y: number): number => x / y;

可选参数和默认参数

1
2
3
4
5
6
7
function createUser(
name: string,
age?: number, // 可选参数
country: string = "CN" // 默认参数
): void {
console.log(`Name: ${name}, Age: ${age}, Country: ${country}`);
}

剩余参数

1
2
3
4
5
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 15

函数重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 重载签名
function processInput(input: string): string;
function processInput(input: number): number;
function processInput(input: boolean): boolean;

// 实现签名
function processInput(input: any): any {
if (typeof input === "string") {
return input.toUpperCase();
} else if (typeof input === "number") {
return input * 2;
} else if (typeof input === "boolean") {
return !input;
}
}

console.log(processInput("hello")); // "HELLO"
console.log(processInput(10)); // 20

第二部分:面向对象编程

5. 接口(Interface)

基础接口

1
2
3
4
5
6
7
8
9
10
11
12
interface User {
readonly id: number; // 只读属性
name: string;
age?: number; // 可选属性
[propName: string]: any; // 索引签名
}

const user: User = {
id: 1,
name: "John",
email: "john@example.com" // 允许额外属性
};

函数类型接口

1
2
3
4
5
6
7
interface SearchFunc {
(source: string, subString: string): boolean;
}

const mySearch: SearchFunc = function(src, sub) {
return src.search(sub) > -1;
};

类类型接口

1
2
3
4
5
6
7
8
9
10
11
12
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}

class Clock implements ClockInterface {
currentTime: Date = new Date();

setTime(d: Date): void {
this.currentTime = d;
}
}

接口继承

1
2
3
4
5
6
7
8
9
10
11
12
interface Shape {
color: string;
}

interface Square extends Shape {
sideLength: number;
}

const square: Square = {
color: "red",
sideLength: 10
};

6. 类(Class)

基础类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Animal {
// 属性
name: string;
private age: number; // 私有属性
protected species: string; // 受保护属性

// 构造函数
constructor(name: string, age: number, species: string) {
this.name = name;
this.age = age;
this.species = species;
}

// 方法
public move(distance: number = 0): void {
console.log(`${this.name} moved ${distance}m.`);
}

// 获取私有属性
public getAge(): number {
return this.age;
}
}

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Dog extends Animal {
private breed: string;

constructor(name: string, age: number, breed: string) {
super(name, age, "Canine");
this.breed = breed;
}

bark(): void {
console.log("Woof! Woof!");
// console.log(this.age); // 错误:age 是私有的
console.log(this.species); // 正确:species 是受保护的
}
}

抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
abstract class Department {
constructor(public name: string) {}

abstract printMeeting(): void; // 抽象方法

printName(): void {
console.log("Department name: " + this.name);
}
}

class AccountingDepartment extends Department {
constructor() {
super("Accounting and Auditing");
}

printMeeting(): void {
console.log("The Accounting Department meets each Monday at 10am.");
}
}

存取器(getter/setter)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Employee {
private _fullName: string = "";

get fullName(): string {
return this._fullName;
}

set fullName(newName: string) {
if (newName && newName.length > 3) {
this._fullName = newName;
} else {
console.log("Error: Name is too short");
}
}
}

静态属性

1
2
3
4
5
6
7
8
9
10
11
class Grid {
static origin = { x: 0, y: 0 };

constructor(public scale: number) {}

calculateDistance(point: { x: number; y: number }): number {
let xDist = point.x - Grid.origin.x;
let yDist = point.y - Grid.origin.y;
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
}

7. 泛型(Generics)

泛型函数

1
2
3
4
5
6
7
function identity<T>(arg: T): T {
return arg;
}

// 使用
let output1 = identity<string>("myString");
let output2 = identity("myString"); // 类型推断

泛型接口

1
2
3
4
5
6
7
8
9
interface GenericIdentityFn<T> {
(arg: T): T;
}

function identity<T>(arg: T): T {
return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;

泛型类

1
2
3
4
5
6
7
8
9
10
11
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;

constructor(zeroValue: T, add: (x: T, y: T) => T) {
this.zeroValue = zeroValue;
this.add = add;
}
}

let myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y);

泛型约束

1
2
3
4
5
6
7
8
9
10
11
interface Lengthwise {
length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}

// loggingIdentity(3); // 错误:数字没有 length 属性
loggingIdentity("hello"); // 正确

第三部分:高级类型

8. 高级类型特性

联合类型和类型守卫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 联合类型
let value: string | number;
value = "hello";
value = 42;

// 类型守卫
function padLeft(value: string, padding: string | number): string {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}

类型别名

1
2
3
4
5
6
7
8
type StringOrNumber = string | number;
type Text = string | { text: string };
type Nameable = { name: string };
type Tree<T> = {
value: T;
left?: Tree<T>;
right?: Tree<T>;
};

字面量类型

1
2
3
4
5
6
7
8
9
type Direction = "north" | "east" | "south" | "west";
type DiceValue = 1 | 2 | 3 | 4 | 5 | 6;

function move(direction: Direction): void {
console.log(`Moving ${direction}`);
}

move("north"); // 正确
// move("up"); // 错误

交叉类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface BusinessPartner {
name: string;
credit: number;
}

interface Identity {
id: number;
name: string;
}

interface Contact {
email: string;
phone: string;
}

type Employee = Identity & Contact;
type Customer = BusinessPartner & Contact;

const employee: Employee = {
id: 1,
name: "John",
email: "john@example.com",
phone: "123-456-7890"
};

9. 映射类型和条件类型

映射类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};

type Partial<T> = {
[P in keyof T]?: T[P];
};

type Person = {
name: string;
age: number;
};

type ReadonlyPerson = Readonly<Person>;
type PartialPerson = Partial<Person>;

条件类型

1
2
3
4
5
6
7
8
9
10
11
type TypeName<T> = 
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";

type T0 = TypeName<string>; // "string"
type T1 = TypeName<"hello">; // "string"
type T2 = TypeName<true>; // "boolean"

实用工具类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}

// 所有属性变为可选
type PartialUser = Partial<User>;

// 所有属性变为只读
type ReadonlyUser = Readonly<User>;

// 选择部分属性
type UserPreview = Pick<User, "id" | "name">;

// 排除某些属性
type UserWithoutId = Omit<User, "id">;

// 提取函数返回类型
type PromiseType = ReturnType<() => Promise<string>>; // Promise<string>

第四部分:模块和命名空间

10. 模块系统

导出和导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// math.ts - 导出
export const PI = 3.14159;

export function calculateCircumference(diameter: number): number {
return diameter * PI;
}

export default class Calculator {
static add(x: number, y: number): number {
return x + y;
}
}

// app.ts - 导入
import Calculator, { PI, calculateCircumference } from './math';
import * as MathUtils from './math';

console.log(PI);
console.log(calculateCircumference(10));
console.log(Calculator.add(5, 3));

重新导出

1
2
3
4
5
6
7
8
// shapes.ts
export class Circle { /* ... */ }
export class Square { /* ... */ }

// index.ts - 重新导出
export { Circle } from './shapes';
export { Square } from './shapes';
export * from './shapes';

11. 命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}

const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;

export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string): boolean {
return lettersRegexp.test(s);
}
}

export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string): boolean {
return s.length === 5 && numberRegexp.test(s);
}
}
}

// 使用
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();

第五部分:实战应用

12. 配置和最佳实践

完整的 tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}

实用工具函数示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 异步错误处理
async function fetchData<T>(url: string): Promise<T> {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json() as T;
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
}

// 类型安全的 EventEmitter
interface EventMap {
'userCreated': { id: number; name: string };
'userDeleted': number;
}

class EventEmitter<T extends Record<string, any>> {
private listeners: { [K in keyof T]?: ((data: T[K]) => void)[] } = {};

on<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
}

emit<K extends keyof T>(event: K, data: T[K]): void {
this.listeners[event]?.forEach(listener => listener(data));
}
}

const emitter = new EventEmitter<EventMap>();
emitter.on('userCreated', (user) => {
console.log(`User created: ${user.name}`);
});

13. 学习路径建议

  1. 第一周:基础类型、函数、接口
  2. 第二周:类、继承、修饰符
  3. 第三周:泛型、高级类型
  4. 第四周:模块、命名空间、配置
  5. 第五周:实战项目,结合 React/Angular/Vue
  6. 第六周:深入学习类型系统,工具类型

14. 常见错误和解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 错误:对象可能为 undefined
function getUserName(user?: { name: string }): string {
// return user.name; // 错误
return user?.name ?? "Unknown"; // 正确:使用可选链和空值合并
}

// 错误:类型断言的安全性
const element = document.getElementById("myInput") as HTMLInputElement;

// 正确:类型守卫
function isInputElement(el: HTMLElement): el is HTMLInputElement {
return el.tagName === "INPUT";
}

const el = document.getElementById("myInput");
if (isInputElement(el)) {
console.log(el.value); // 安全访问
}