TypeScript 教程 - JavaScript 的超集
项目概述
TypeScript 是 JavaScript 的超集,它添加了静态类型检查和其他特性,提高了代码的可维护性和可扩展性。TypeScript 代码最终会被编译为 JavaScript 代码,使其在任何 JavaScript 运行环境中运行。
GitHub Stars:96k+
适用环境:JavaScript 项目、大型应用
核心特点
类型系统:添加静态类型检查,提高代码质量
接口:定义对象的结构和类型
泛型:支持泛型编程,提高代码复用性
命名空间:提供命名空间,避免命名冲突
模块:支持模块化编程,提高代码组织性
装饰器:支持装饰器语法,用于元编程
类型推断:自动推断变量类型,减少类型注解
类型别名:支持类型别名,提高代码可读性
联合类型:支持联合类型,增加类型灵活性
交叉类型:支持交叉类型,组合多个类型
安装设置
1. 局部安装(推荐)
在项目中局部安装 TypeScript:
# 使用 npm
npm install --save-dev typescript
# 使用 yarn
yarn add --dev typescript
# 使用 pnpm
pnpm add --save-dev typescript2. 全局安装
在系统中全局安装 TypeScript:
# 使用 npm
npm install -g typescript
# 使用 yarn
yarn global add typescript
# 使用 pnpm
pnpm add -g typescript3. 初始化配置
初始化 TypeScript 配置:
# 在项目根目录执行
npx tsc --init
# 或使用全局安装的 TypeScript
tsc --init这会生成 tsconfig.json 文件,包含 TypeScript 的配置选项。
4. 基本配置
修改 tsconfig.json 文件,配置 TypeScript:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2015", "dom"],
"allowJs": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}基本使用
1. 编译 TypeScript 文件
编译单个 TypeScript 文件或目录:
# 编译单个文件
npx tsc src/file.ts
# 编译目录
npx tsc
# 监视文件变化并自动编译
npx tsc --watch2. 配置脚本
在 package.json 中配置脚本:
{
"scripts": {
"build": "tsc",
"build:watch": "tsc --watch",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts"
}
}然后可以通过以下命令执行:
# 编译代码
npm run build
# 监视模式编译
npm run build:watch
# 运行编译后的代码
npm start
# 使用 ts-node 直接运行 TypeScript 代码
npm run dev3. 基本类型
TypeScript 支持以下基本类型:
// 布尔类型
let isDone: boolean = false;
// 数字类型
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
// 字符串类型
let color: string = "blue";
let fullName: string = `Bob Bobbington`;
// 数组类型
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
// 元组类型
let x: [string, number];
x = ["hello", 10];
// 枚举类型
enum Color {
Red,
Green,
Blue
}
let c: Color = Color.Green;
// 任意类型
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;
// 空类型
let unusable: void = undefined;
// null 和 undefined 类型
let u: undefined = undefined;
let n: null = null;
// never 类型
function error(message: string): never {
throw new Error(message);
}
// object 类型
let obj: object = {a: 1, b: 2};高级功能
1. 接口
接口定义了对象的结构和类型:
// 基本接口
interface Person {
name: string;
age: number;
}
let person: Person = {
name: "John",
age: 30
};
// 可选属性
interface Person {
name: string;
age?: number;
}
// 只读属性
interface Person {
readonly id: number;
name: string;
}
// 函数接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(source: string, subString: string): boolean {
return source.includes(subString);
};
// 可索引接口
interface StringArray {
[index: number]: string;
}
let myArray: StringArray = ["Bob", "Fred"];
// 类接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) {}
}
// 继承接口
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square: Square = {
color: "blue",
sideLength: 10
};2. 泛型
泛型支持泛型编程,提高代码复用性:
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString");
let output2 = identity<number>(100);
// 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
// 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
// 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity("hello");
loggingIdentity([1, 2, 3]);
// 泛型参数默认类型
function createArray<T = string>(length: number, value: T): T[] {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
let strings = createArray(3, "hello");
let numbers = createArray<number>(3, 100);3. 命名空间
命名空间提供命名空间,避免命名冲突:
// 基本命名空间
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) {
return lettersRegexp.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
// 使用命名空间
let strings = ["Hello", "98052", "101"];
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// 嵌套命名空间
namespace Validation {
export namespace Letters {
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
}
// 别名
import val = Validation.Letters.LettersOnlyValidator;
let validator = new val();4. 模块
模块支持模块化编程,提高代码组织性:
// 导出模块
// validation.ts
export interface StringValidator {
isAcceptable(s: string): boolean;
}
export const lettersRegexp = /^[A-Za-z]+$/;
export const numberRegexp = /^[0-9]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
// 导入模块
// app.ts
import { StringValidator, LettersOnlyValidator, ZipCodeValidator } from "./validation";
let strings = ["Hello", "98052", "101"];
let validators: { [s: string]: StringValidator } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();
// 导入整个模块
import * as Validation from "./validation";
let validator = new Validation.LettersOnlyValidator();
// 导出默认
// my-module.ts
export default function (x: number): number {
return x + 1;
}
// 导入默认
import add from "./my-module";
add(1); // 2
// 重新导出
export { LettersOnlyValidator as RegExpBasedValidator } from "./validation";
// 导出 * 从
import * as validation from "./validation";
export { validation };5. 装饰器
装饰器支持装饰器语法,用于元编程:
// 类装饰器
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
// 方法装饰器
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
// 访问器装饰器
function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() { return this._x; }
@configurable(false)
get y() { return this._y; }
}
// 属性装饰器
function format(formatString: string) {
return function (target: any, propertyKey: string) {
// 实现装饰器逻辑
};
}
class Greeter {
@format("Hello, %s")
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
// 参数装饰器
function required(target: any, propertyKey: string, parameterIndex: number) {
// 实现装饰器逻辑
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet(@required name: string) {
return "Hello, " + name + ", " + this.greeting;
}
}实际应用场景
1. React 项目
场景:在 React 项目中使用 TypeScript 提高代码质量
实现步骤:
- 安装必要的依赖:
npm install --save-dev typescript @types/react @types/react-dom- 配置 TypeScript:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}- 编写 TypeScript React 组件:
import React from 'react';
interface Props {
name: string;
age?: number;
onSubmit: (data: { name: string; age: number }) => void;
}
const UserForm: React.FC<Props> = ({ name, age = 18, onSubmit }) => {
const [formData, setFormData] = React.useState({ name, age });
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'number' ? parseInt(value) : value
}));
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSubmit(formData);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name:</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
</div>
<div>
<label>Age:</label>
<input
type="number"
name="age"
value={formData.age}
onChange={handleChange}
/>
</div>
<button type="submit">Submit</button>
</form>
);
};
export default UserForm;2. Node.js 项目
场景:在 Node.js 项目中使用 TypeScript 提高代码质量
实现步骤:
- 安装必要的依赖:
npm install --save-dev typescript @types/node
npm install --save-dev ts-node- 配置 TypeScript:
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}- 编写 TypeScript Node.js 代码:
// src/index.ts
import express from 'express';
import { Request, Response } from 'express';
const app = express();
const port = 3000;
app.use(express.json());
interface User {
id: number;
name: string;
email: string;
}
let users: User[] = [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Doe', email: 'jane@example.com' }
];
app.get('/users', (req: Request, res: Response) => {
res.json(users);
});
app.get('/users/:id', (req: Request, res: Response) => {
const id = parseInt(req.params.id);
const user = users.find(user => user.id === id);
if (user) {
res.json(user);
} else {
res.status(404).json({ message: 'User not found' });
}
});
app.post('/users', (req: Request, res: Response) => {
const newUser: User = {
id: users.length + 1,
name: req.body.name,
email: req.body.email
};
users.push(newUser);
res.status(201).json(newUser);
});
app.put('/users/:id', (req: Request, res: Response) => {
const id = parseInt(req.params.id);
const userIndex = users.findIndex(user => user.id === id);
if (userIndex !== -1) {
users[userIndex] = {
...users[userIndex],
name: req.body.name,
email: req.body.email
};
res.json(users[userIndex]);
} else {
res.status(404).json({ message: 'User not found' });
}
});
app.delete('/users/:id', (req: Request, res: Response) => {
const id = parseInt(req.params.id);
const userIndex = users.findIndex(user => user.id === id);
if (userIndex !== -1) {
users.splice(userIndex, 1);
res.json({ message: 'User deleted' });
} else {
res.status(404).json({ message: 'User not found' });
}
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});3. 工具库项目
场景:创建一个 TypeScript 工具库,提高代码复用性
实现步骤:
- 安装必要的依赖:
npm install --save-dev typescript- 配置 TypeScript:
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"declaration": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}- 编写 TypeScript 工具库:
// src/utils.ts
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout;
return (...args: Parameters<T>) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
export function throttle<T extends (...args: any[]) => any>(
func: T,
limit: number
): (...args: Parameters<T>) => void {
let inThrottle: boolean;
return (...args: Parameters<T>) => {
if (!inThrottle) {
func(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
export function deepClone<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime()) as unknown as T;
}
if (obj instanceof Array) {
return obj.map(item => deepClone(item)) as unknown as T;
}
if (typeof obj === 'object') {
const clonedObj = {} as T;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key]);
}
}
return clonedObj;
}
return obj;
}
export function formatDate(date: Date, format: string): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', year.toString())
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}最佳实践
启用严格模式:启用
strict: true,获得最大的类型安全性使用类型推断:利用 TypeScript 的类型推断,减少类型注解
定义明确的接口:为复杂对象定义明确的接口,提高代码可读性
使用泛型:使用泛型提高代码复用性
合理使用 any:尽量避免使用
any类型,保持类型安全性使用联合类型:使用联合类型增加类型灵活性
使用类型别名:使用类型别名提高代码可读性
模块化组织:使用模块和命名空间组织代码,提高代码可维护性
使用装饰器:合理使用装饰器,简化代码
编写类型声明文件:为第三方库编写类型声明文件,提高类型安全性
常见问题与解决方案
1. 类型错误
问题:TypeScript 类型错误,导致编译失败
解决方案:
- 检查类型注解是否正确
- 使用类型断言解决类型不匹配问题
- 调整类型定义,使其更准确
- 使用联合类型处理多种可能的类型
2. 模块解析问题
问题:模块解析失败,找不到模块
解决方案:
- 检查模块路径是否正确
- 配置
baseUrl和paths选项 - 确保安装了必要的类型声明文件
- 检查
moduleResolution配置
3. 编译速度问题
问题:TypeScript 编译速度慢
解决方案:
- 合理配置
include和exclude选项 - 使用
tsconfig.json的references功能拆分配置 - 考虑使用
esbuild或swc等更快的编译器 - 启用
incremental编译选项
4. 与 JavaScript 集成问题
问题:与现有 JavaScript 代码集成困难
解决方案:
- 使用
allowJs: true允许混合使用 JavaScript 和 TypeScript - 为 JavaScript 代码编写类型声明文件
- 逐步迁移 JavaScript 代码到 TypeScript
5. 类型声明文件问题
问题:缺少第三方库的类型声明文件
解决方案:
- 安装
@types/xxx类型声明文件 - 为第三方库编写自定义类型声明文件
- 使用
declare module声明模块
参考资源
GitHub 仓库:https://github.com/microsoft/TypeScript
TypeScript 手册:https://www.typescriptlang.org/docs/handbook/intro.html
TypeScript playground:https://www.typescriptlang.org/play/
教程资源:
- TypeScript 官方教程: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html
- TypeScript 与 React 集成: https://www.typescriptlang.org/docs/handbook/react.html
- TypeScript 与 Node.js 集成: https://www.typescriptlang.org/docs/handbook/nodejs.html
总结
TypeScript 是一个功能强大的 JavaScript 超集,它添加了静态类型检查和其他特性,提高了代码的可维护性和可扩展性。通过本文的介绍,你应该已经了解了 TypeScript 的核心功能、使用方法和最佳实践。
TypeScript 的优势在于:
类型安全:静态类型检查,减少运行时错误
代码可维护性:明确的类型定义,提高代码可读性
代码可扩展性:接口、泛型等特性,提高代码复用性
工具支持:丰富的 IDE 支持,提供代码提示和重构功能
生态系统:与 JavaScript 生态系统完全兼容,支持所有 JavaScript 库
TypeScript 适合各种项目,尤其是大型应用和团队协作项目。它不仅可以提高代码质量,还可以减少开发过程中的错误,提高开发效率。
如果你还没有在项目中使用 TypeScript,建议你尽快尝试,相信它会给你的开发工作带来很大的帮助。