TypeScript进阶:从入门到实践
本文详细介绍了TypeScript的基础概念,包括变量声明、类型注解、函数定义、类与接口的使用。接着深入探讨了高级类型如联合类型、元组类型、枚举类型和类型保护,并进一步讲解了泛型的概念和应用场景。此外,文章还涵盖了模块化编程、装饰器的使用以及TypeScript项目开发的最佳实践,旨在帮助读者掌握TypeScript进阶知识。
1. TypeScript基础回顾1.1 变量声明与类型注解
TypeScript 是一种静态类型语言,它允许开发者在声明变量时指定其类型。类型注解可以提高代码的可读性和可维护性,同时减少运行时错误。
1.1.1 基本类型
TypeScript 支持许多基本数据类型,包括 number
, string
, boolean
, undefined
, null
, any
, void
等。
// 声明一个数字类型的变量
let age: number = 25;
// 声明一个字符串类型的变量
let name: string = "Alice";
// 声明一个布尔类型的变量
let isStudent: boolean = true;
// 声明一个 undefined 类型的变量
let undefinedExample: undefined = undefined;
// 声明一个 null 类型的变量
let nullExample: null = null;
// 声明一个任意类型的变量
let anything: any = 123;
anything = "Hello";
// 声明一个 void 类型的变量
let nothing: void = undefined;
1.1.2 数组类型
数组类型可以通过在类型后面添加方括号 []
来表示。
// 声明一个数字数组
let numbers: number[] = [1, 2, 3];
// 使用泛型数组类型
let numbers2: Array<number> = [4, 5, 6];
// 字符串数组
let names: string[] = ["Alice", "Bob", "Charlie"];
1.2 函数定义及参数类型
函数定义在 TypeScript 中需要明确指定返回类型和参数类型。
// 声明一个带参数和返回值的函数
function add(x: number, y: number): number {
return x + y;
}
// 一个带默认参数的函数
function greet(name: string = "Anonymous"): string {
return `Hello, ${name}`;
}
// 一个带可选参数的函数
function log(message: string, level?: string) {
if (level) {
console.log(`[${level}] ${message}`);
} else {
console.log(message);
}
}
1.3 类与接口的使用
1.3.1 类的定义
类在 TypeScript 中定义类似于其他面向对象语言,可以通过构造函数初始化成员变量。类和接口还可以通过继承来扩展功能。
class Animal {
constructor(public name: string) {}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
speak() {
console.log(`${this.name} barks.`);
}
}
let animal = new Animal('Generic animal');
let dog = new Dog('Rex');
animal.speak();
dog.speak();
1.3.2 接口定义
接口定义一组行为和属性的要求,类可以实现接口来确保遵守这些规则。接口也可以通过继承来扩展功能。
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
2. TypeScript高级类型
2.1 联合类型与元组类型
2.1.1 联合类型
联合类型允许变量同时持有多种类型。使用 |
符号来定义多种类型。
let myNumber: number | string;
myNumber = 123;
myNumber = "Hello";
2.1.2 元组类型
元组是固定长度的数组,每个位置可以有不同的类型。
let myTuple: [number, string];
myTuple = [1, "two"];
// myTuple = [1, 2]; // 错误,第二个元素必须是字符串
2.2 枚举类型的应用
枚举类型允许定义一组命名的常量,常用于表示一组相关的值。
enum Color {Red = 1, Green, Blue}
let currentColor: Color = Color.Red;
console.log(currentColor); // 输出 1
if (currentColor === Color.Red) {
console.log("当前颜色是红色");
}
2.3 类型保护与区分类型
类型保护是一种在运行时确保某个值具有特定类型的机制。
function isNumber(value: any): value is number {
return typeof value === "number";
}
function isString(value: any): value is string {
return typeof value === "string";
}
function processValue(value: any) {
if (isNumber(value)) {
console.log(`${value} 是一个数字`);
} else if (isString(value)) {
console.log(`${value} 是一个字符串`);
}
}
processValue("Hello");
processValue(42);
3. 泛型深入理解
3.1 泛型的基本概念
泛型允许编写可重用的代码,而不需要在编写时知道具体的类型。
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("Hello");
console.log(output); // 输出 "Hello"
3.2 泛型函数与泛型类
3.2.1 泛型函数
泛型函数可以接收任意类型的参数。
function swap<T, U>(t: T, u: U): [U, T] {
return [u, t];
}
let swapped = swap<number, string>(1, "two");
console.log(swapped); // 输出 ["two", 1]
3.2.2 泛型类
泛型类可以使用泛型类型参数。
class GenericCollection<T> {
items: T[] = [];
add(item: T) {
this.items.push(item);
}
getItems(): T[] {
return this.items;
}
}
let numbers = new GenericCollection<number>();
numbers.add(1);
numbers.add(2);
console.log(numbers.getItems()); // 输出 [1, 2]
let strings = new GenericCollection<string>();
strings.add("Hello");
strings.add("World");
console.log(strings.getItems()); // 输出 ["Hello", "World"]
3.3 实战:使用泛型解决通用问题
泛型在解决通用问题、提高代码复用性方面非常有用。
function makeArray<T>(length: number, value: T): T[] {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
let numberArray = makeArray<number>(5, 1);
console.log(numberArray); // 输出 [1, 1, 1, 1, 1]
let stringArray = makeArray<string>(3, "Hello");
console.log(stringArray); // 输出 ["Hello", "Hello", "Hello"]
4. 模块化编程
4.1 ES模块与CommonJS模块的区别
ES模块使用静态导入导出,而CommonJS模块使用动态导入导出。
// ES模块的导入导出
export const PI = 3.14159;
export function square(x: number) {
return x * x;
}
// CommonJS模块的导入导出
module.exports.PI = 3.14159;
module.exports.square = function(x) {
return x * x;
};
4.2 TypeScript模块的导入导出
TypeScript 支持两种模块系统:ES模块和CommonJS模块。
// ES模块导入
import { PI, square } from './math';
console.log(square(PI)); // 输出 9.869604401089358
// CommonJS模块导入
const { PI, square } = require('./math');
console.log(square(PI)); // 输出 9.869604401089358
4.3 模块化编程的优势与实践
模块化编程可以提高代码的可维护性,减少代码的耦合度,便于并行开发。
// 模块化编程示例
// math.ts
export function add(x: number, y: number): number {
return x + y;
}
// main.ts
import { add } from './math';
console.log(add(1, 2)); // 输出 3
5. 装饰器基础与应用
5.1 装饰器的基本概念
装饰器是一种特殊的声明,可以在类、方法、访问器和属性上进行声明。它可以在运行时修改、增强或验证这些声明。装饰器可以使用类型注解来确保其参数和返回值的类型正确。
function readonly(target: any, name: string, descriptor: PropertyDescriptor) {
let originalMethod = descriptor.get;
descriptor.get = function() {
return originalMethod.apply(this);
};
descriptor.set = function() {
throw new Error("不能修改只读属性");
};
return descriptor;
}
class Person {
@readonly
name: string;
constructor(name: string) {
this.name = name;
}
}
let person = new Person("Alice");
console.log(person.name); // 输出 "Alice"
// person.name = "Bob"; // 抛出错误 "不能修改只读属性"
5.2 实战:创建和使用装饰器
创建装饰器可以增强类和方法的功能。
function log(target: any, name: string, descriptor: PropertyDescriptor) {
let originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling method "${name}" with`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
class Greeter {
@log
greet(name: string): string {
return `Hello, ${name}`;
}
}
let greeter = new Greeter();
console.log(greeter.greet("Alice")); // 输出 "Calling method "greet" with [ 'Alice' ]"
5.3 装饰器在项目中的实际应用
装饰器可以用于日志记录、性能监控等场景。
function log(target: any, name: string, descriptor: PropertyDescriptor) {
let originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling method "${name}" with`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
class LoggerService {
@log
logMessage(message: string) {
console.log(message);
}
}
let logger = new LoggerService();
logger.logMessage("这是一个日志消息"); // 输出 "Calling method "logMessage" with [ '这是一个日志消息' ]"
6. TypeScript项目开发的最佳实践
6.1 代码规范与Lint工具
遵循一定的代码规范可以提高团队协作的效率。常用的Lint工具包括 ESLint 和 TSLint。
// TSLint 配置示例
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"strict": true
},
"rules": {
"no-console": "off",
"no-unnecessary-generics": "warn",
"no-unsafe-optional-chaining": "warn",
"no-var-requires": "warn"
}
}
// ESLint 配置示例
module.exports = {
"rules": {
"no-console": "off",
"no-unnecessary-generics": "warn",
"no-unsafe-optional-chaining": "warn",
"no-var-requires": "warn"
}
}
6.2 Type推断和注释的最佳实践
TypeScript 允许推断类型,但明确标注类型可以帮助提高可读性和可维护性。
interface User {
id: number;
name: string;
email: string;
}
let user: User = {
id: 1,
name: "Alice",
email: "alice@example.com"
};
function getUser(id: number): User {
return {
id: id,
name: "Alice",
email: "alice@example.com"
};
}
let user2 = getUser(1); // 类型推断
console.log(user2); // 输出 { id: 1, name: "Alice", email: "alice@example.com" }
6.3 项目构建与部署
使用构建工具如 Webpack 或 Rollup 可以将 TypeScript 代码编译成 JavaScript 代码并进行优化。
// Webpack 配置示例
const path = require("path");
module.exports = {
mode: "production",
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
exclude: /node_modules/
}
]
},
resolve: {
extensions: [".ts", ".js"]
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist")
}
};
通过以上内容,我们详细介绍了 TypeScript 的基础概念、高级类型、泛型、模块化编程、装饰器以及项目开发的最佳实践。希望这些知识能帮助你更好地理解和使用 TypeScript。
共同学习,写下你的评论
评论加载中...
作者其他优质文章