TypeScript
一、TypeScript 基础
1. TypeScript 是什么?
TypeScript 是 JavaScript 的超集,添加了静态类型系统。
核心特性:
- 静态类型检查:编译时发现错误
- 类型注解:为变量、函数等添加类型
- 接口和类型:定义数据结构
- 泛型:支持类型参数化
- ES6+ 支持:支持最新的 JavaScript 特性
2. 为什么使用 TypeScript?
优势:
- 早期发现错误,减少运行时错误
- 更好的 IDE 支持和代码提示
- 代码更易维护和重构
- 更好的文档性(类型即文档)
劣势:
- 需要编译步骤
- 学习曲线
- 对于小项目可能过度设计
二、基本类型
1. 原始类型
typescript
// 数字
let count: number = 10;
// 字符串
let name: string = "Alice";
// 布尔值
let isActive: boolean = true;
// null 和 undefined
let data: null = null;
let value: undefined = undefined;
// Symbol
let sym: symbol = Symbol("key");
// BigInt
let big: bigint = 100n;2. 数组和元组
typescript
// 数组
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ["Alice", "Bob"];
// 元组
let tuple: [string, number] = ["Alice", 20];
tuple[0] = "Bob";
tuple[1] = 21;
// 可选元组
let optionalTuple: [string, number?] = ["Alice"];3. 对象类型
typescript
// 对象字面量
let user: { name: string; age: number } = {
name: "Alice",
age: 20
};
// 可选属性
let user2: { name: string; age?: number } = {
name: "Alice"
};
// 只读属性
let config: { readonly apiKey: string } = {
apiKey: "123456"
};4. 枚举(Enum)
typescript
// 数字枚举
enum Status {
Pending,
Approved,
Rejected
}
let status: Status = Status.Pending;
// 字符串枚举
enum Color {
Red = "red",
Green = "green",
Blue = "blue"
}
// 常量枚举(编译时内联)
const enum Direction {
Up,
Down,
Left,
Right
}5. Any 和 Unknown
typescript
// any:任意类型,失去类型检查
let value: any = "hello";
value = 123;
value = true;
// unknown:类型安全的 any
let value2: unknown = "hello";
// value2.toUpperCase(); // 错误:需要类型检查
if (typeof value2 === "string") {
value2.toUpperCase(); // 正确
}6. Void 和 Never
typescript
// void:函数没有返回值
function log(message: string): void {
console.log(message);
}
// never:函数永远不会返回
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}三、函数类型
1. 函数声明
typescript
// 函数参数和返回值类型
function add(a: number, b: number): number {
return a + b;
}
// 可选参数
function greet(name: string, age?: number): string {
if (age) {
return `Hello, ${name}, you are ${age} years old`;
}
return `Hello, ${name}`;
}
// 默认参数
function multiply(a: number, b: number = 1): number {
return a * b;
}
// 剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}2. 函数表达式
typescript
// 箭头函数
const add = (a: number, b: number): number => {
return a + b;
};
// 函数类型
type AddFunction = (a: number, b: number) => number;
const multiply: AddFunction = (a, b) => {
return a * b;
};3. 函数重载
typescript
function process(value: string): string;
function process(value: number): number;
function process(value: string | number): string | number {
if (typeof value === "string") {
return value.toUpperCase();
}
return value * 2;
}
const result1 = process("hello"); // string
const result2 = process(10); // number四、接口和类型别名
1. 接口(Interface)
typescript
interface User {
name: string;
age: number;
email?: string; // 可选属性
readonly id: number; // 只读属性
}
const user: User = {
name: "Alice",
age: 20,
id: 1
};
// 接口扩展
interface Admin extends User {
role: string;
permissions: string[];
}2. 类型别名(Type)
typescript
type User = {
name: string;
age: number;
};
// 联合类型
type Status = "pending" | "approved" | "rejected";
// 交叉类型
type Admin = User & {
role: string;
};3. Interface vs Type
相同点:
- 都可以定义对象类型
- 都支持扩展
不同点:
- Interface 支持声明合并,Type 不支持
- Type 可以定义联合类型、交叉类型等,Interface 主要用于对象
typescript
// Interface 声明合并
interface Window {
customProperty: string;
}
interface Window {
anotherProperty: number;
}
// Type 联合类型
type ID = string | number;
type Status = "active" | "inactive";五、泛型
1. 泛型函数
typescript
// 泛型函数
function identity<T>(value: T): T {
return value;
}
const result1 = identity<string>("hello");
const result2 = identity<number>(123);
const result3 = identity("hello"); // 类型推断
// 多个类型参数
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}2. 泛型接口
typescript
interface Repository<T> {
findById(id: number): T | undefined;
findAll(): T[];
save(entity: T): void;
}
class UserRepository implements Repository<User> {
findById(id: number): User | undefined {
// ...
}
findAll(): User[] {
// ...
}
save(entity: User): void {
// ...
}
}3. 泛型类
typescript
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
}
const container = new Container<string>("hello");4. 泛型约束
typescript
// 约束类型参数必须有 length 属性
function logLength<T extends { length: number }>(value: T): void {
console.log(value.length);
}
logLength("hello"); // 5
logLength([1, 2, 3]); // 3
// 使用 keyof 约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Alice", age: 20 };
const name = getProperty(user, "name"); // string六、高级类型
1. 联合类型和交叉类型
typescript
// 联合类型
type ID = string | number;
function processId(id: ID) {
if (typeof id === "string") {
return id.toUpperCase();
}
return id.toString();
}
// 交叉类型
type Admin = User & {
role: string;
permissions: string[];
};2. 类型断言
typescript
// as 语法
const value = "hello" as string;
// <类型> 语法
const element = <HTMLInputElement>document.getElementById("input");
// 非空断言
const user = users.find(u => u.id === 1)!;3. 类型保护
typescript
// typeof 类型保护
function isString(value: unknown): value is string {
return typeof value === "string";
}
// instanceof 类型保护
class Dog {
bark() {}
}
class Cat {
meow() {}
}
function isDog(animal: Dog | Cat): animal is Dog {
return animal instanceof Dog;
}
// 自定义类型保护
interface Fish {
swim(): void;
}
interface Bird {
fly(): void;
}
function isFish(animal: Fish | Bird): animal is Fish {
return (animal as Fish).swim !== undefined;
}4. 映射类型
typescript
// Partial:所有属性变为可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Readonly:所有属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// Pick:选择部分属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
// Omit:排除部分属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
// 使用示例
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
type UserName = Pick<User, "name">;
type UserWithoutId = Omit<User, "id">;5. 条件类型
typescript
// 条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
type Example1 = NonNullable<string | null>; // string
type Example2 = NonNullable<number | undefined>; // number
// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;
type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]七、类
1. 类的定义
typescript
class User {
// 属性
private name: string;
public age: number;
protected email: string;
readonly id: number;
// 构造函数
constructor(name: string, age: number, email: string) {
this.name = name;
this.age = age;
this.email = email;
this.id = Math.random();
}
// 方法
getName(): string {
return this.name;
}
// 静态方法
static createAdmin(name: string): User {
const user = new User(name, 0, "");
return user;
}
}2. 访问修饰符
public(默认):公共的,任何地方都可以访问private:私有的,只能在类内部访问protected:受保护的,可以在类内部和子类中访问
typescript
class Animal {
public name: string;
private age: number;
protected species: string;
constructor(name: string, age: number, species: string) {
this.name = name;
this.age = age;
this.species = species;
}
}
class Dog extends Animal {
constructor(name: string, age: number) {
super(name, age, "dog");
console.log(this.species); // 可以访问 protected
}
}3. 抽象类
typescript
abstract class Shape {
abstract area(): number;
getPerimeter(): number {
return 0;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
area(): number {
return Math.PI * this.radius ** 2;
}
}4. 接口实现
typescript
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
class Duck implements Flyable, Swimmable {
fly(): void {
console.log("Duck is flying");
}
swim(): void {
console.log("Duck is swimming");
}
}八、模块
1. 导出和导入
typescript
// 导出
export const PI = 3.14;
export function add(a: number, b: number): number {
return a + b;
}
export class User {}
// 默认导出
export default class Admin {}
// 导入
import { PI, add, User } from "./utils";
import Admin from "./admin";
import * as Utils from "./utils";
// 重新导出
export { User } from "./user";
export * from "./types";2. 命名空间
typescript
namespace MathUtils {
export function add(a: number, b: number): number {
return a + b;
}
export function multiply(a: number, b: number): number {
return a * b;
}
}
// 使用
MathUtils.add(1, 2);九、工具类型
1. 常用工具类型
typescript
// Partial<T>:所有属性变为可选
type PartialUser = Partial<User>;
// Required<T>:所有属性变为必填
type RequiredUser = Required<User>;
// Readonly<T>:所有属性变为只读
type ReadonlyUser = Readonly<User>;
// Pick<T, K>:选择部分属性
type UserName = Pick<User, "name">;
// Omit<T, K>:排除部分属性
type UserWithoutId = Omit<User, "id">;
// Record<K, T>:创建记录类型
type UserMap = Record<string, User>;
// Exclude<T, U>:从 T 中排除 U
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// Extract<T, U>:从 T 中提取 U
type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable<T>:排除 null 和 undefined
type T2 = NonNullable<string | null | undefined>; // string
// ReturnType<T>:获取函数返回类型
type T3 = ReturnType<() => string>; // string
// Parameters<T>:获取函数参数类型
type T4 = Parameters<(a: number, b: string) => void>; // [number, string]十、常见面试题
1. TypeScript 和 JavaScript 的区别?
| 特性 | TypeScript | JavaScript |
|---|---|---|
| 类型系统 | 静态类型 | 动态类型 |
| 编译 | 需要编译 | 直接运行 |
| 类型检查 | 编译时 | 运行时 |
| IDE 支持 | 更好 | 一般 |
| 学习曲线 | 较陡 | 平缓 |
2. any 和 unknown 的区别?
any:任意类型,失去类型检查,不推荐使用unknown:类型安全的any,需要类型检查后才能使用
typescript
let value1: any = "hello";
value1.toUpperCase(); // 可以,但不安全
let value2: unknown = "hello";
// value2.toUpperCase(); // 错误:需要类型检查
if (typeof value2 === "string") {
value2.toUpperCase(); // 正确
}3. interface 和 type 的区别?
- Interface 支持声明合并,Type 不支持
- Type 可以定义联合类型、交叉类型等,Interface 主要用于对象
- Interface 可以通过
extends扩展,Type 使用&交叉类型
4. 如何理解泛型?
泛型是类型参数化,允许在使用时才指定类型,提供类型安全的同时保持代码的灵活性。
typescript
function identity<T>(value: T): T {
return value;
}
// T 是类型参数,可以传入任意类型
const result1 = identity<string>("hello");
const result2 = identity<number>(123);