主题模式
TypeScript深入解析:从基础到高级特性的全面指南
TypeScript是Microsoft开发的开源编程语言,它是JavaScript的一个超集,添加了可选的静态类型和基于类的面向对象编程。TypeScript可以编译成纯JavaScript,并且可以在任何浏览器、Node.js或任何支持ECMAScript 3(或更高版本)的JavaScript引擎中运行。本文将详细介绍TypeScript的基础知识和高级特性。
1. TypeScript简介
TypeScript由Anders Hejlsberg(C#之父)领导开发,于2012年首次发布。它旨在解决JavaScript在大型应用开发中的痛点,通过引入静态类型检查来提高代码质量和开发效率。
1.1 TypeScript的主要特性
- 静态类型检查:在编译时发现类型错误
- 类型推断:自动推断变量类型
- 接口和类:支持面向对象编程
- 泛型:创建可重用的组件
- 装饰器:为类声明和成员添加注解
- 模块系统:支持ES6模块和CommonJS
- 装饰器:为类声明和成员添加元编程能力
1.2 TypeScript与JavaScript的关系
javascript
// JavaScript代码
function greet(person, date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", Date()); // 错误调用,Date()返回字符串而不是Date对象typescript
// TypeScript代码
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", new Date()); // 正确调用
greet("Maddison", Date()); // 编译时报错2. TypeScript环境搭建
2.1 安装TypeScript
bash
# 全局安装TypeScript
npm install -g typescript
# 或者作为开发依赖安装到项目中
npm install --save-dev typescript
# 验证安装
tsc --version2.2 初始化TypeScript项目
bash
# 创建tsconfig.json配置文件
tsc --init
# 或者手动创建配置文件
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}2.3 编译TypeScript代码
bash
# 编译单个文件
tsc hello.ts
# 编译整个项目
tsc
# 监听模式编译
tsc --watch
# 使用配置文件编译
tsc --project tsconfig.json3. TypeScript基础类型
3.1 基本类型
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";
color = 'red';
// 模板字符串
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}. I'll be ${age + 1} years old next month.`;
// 数组
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
// 元组 Tuple
let x: [string, number];
x = ['hello', 10]; // OK
// x = [10, 'hello']; // Error
// 枚举
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
// 枚举默认从0开始编号,也可以手动赋值
enum Color2 {Red = 1, Green, Blue}
let c2: Color2 = Color2.Green;
// Any类型
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
// Void类型
function warnUser(): void {
console.log("This is my warning message");
}
// Null 和 Undefined
let u: undefined = undefined;
let n: null = null;
// Never类型
function error(message: string): never {
throw new Error(message);
}
// Object类型
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK3.2 类型断言
typescript
// "尖括号"语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as语法
let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;4. 接口(Interfaces)
4.1 基本接口
typescript
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = { firstName: "Jane", lastName: "User" };
console.log(greeter(user));4.2 可选属性
typescript
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"});4.3 只读属性
typescript
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
// p1.x = 5; // Error!4.4 函数类型
typescript
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
}4.5 可索引类型
typescript
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];4.6 类类型
typescript
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) { }
}5. 类(Classes)
5.1 基本类定义
typescript
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");5.2 继承
typescript
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
sam.move(10);
tom.move(34);5.3 访问修饰符
typescript
class Animal {
private name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
}
class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }
}
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino;
// animal = employee; // Error: 'Animal' and 'Employee' are not compatible5.4 Readonly修饰符
typescript
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
// dad.name = "Man with the 3-piece suit"; // Error! name is readonly5.5 存取器
typescript
class Employee {
private _fullName: string = "";
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (newName && newName.length > 0) {
this._fullName = newName;
} else {
console.log("Error: fullName cannot be empty");
}
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
console.log(employee.fullName);
}6. 泛型(Generics)
6.1 基本泛型
typescript
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString");
let output2 = identity("myString"); // 类型推断6.2 泛型接口
typescript
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;6.3 泛型类
typescript
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; };6.4 泛型约束
typescript
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
loggingIdentity(3); // Error, number doesn't have a .length property
loggingIdentity({length: 10, value: 3}); // OK7. 高级类型
7.1 交叉类型
typescript
interface Person {
name: string;
}
interface Serializable {
serialize(): string;
}
// 交叉类型
type LoggablePerson = Person & Serializable;
class Employee implements LoggablePerson {
name: string;
constructor(name: string) {
this.name = name;
}
serialize(): string {
return JSON.stringify(this);
}
}7.2 联合类型
typescript
function padLeft(value: string, padding: string | number) {
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}'.`);
}7.3 类型保护
typescript
// typeof类型保护
function isNumber(x: any): x is number {
return typeof x === "number";
}
// instanceof类型保护
class Bird {
fly() {}
layEggs() {}
}
class Fish {
swim() {}
layEggs() {}
}
function getRandomPet(): Bird | Fish {
return Math.random() > 0.5 ? new Bird() : new Fish();
}
let pet = getRandomPet();
if (pet instanceof Bird) {
pet.fly();
} else if (pet instanceof Fish) {
pet.swim();
}7.4 可辨识联合
typescript
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
}
}8. 装饰器(Decorators)
8.1 类装饰器
typescript
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;
}
}8.2 方法装饰器
typescript
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;
}
}8.3 属性装饰器
typescript
function format(formatString: string) {
return function (target: any, propertyKey: string) {
// 属性装饰器逻辑
};
}
class Greeter {
@format("Hello, %s")
greeting: string;
constructor(message: string) {
this.greeting = message;
}
}9. 模块和命名空间
9.1 ES6模块
typescript
// math.ts
export function add(x: number, y: number): number {
return x + y;
}
export function subtract(x: number, y: number): number {
return x - y;
}
// main.ts
import { add, subtract } from './math';
console.log(add(1, 2)); // 39.2 命名空间
typescript
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 validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();10. TypeScript配置
10.1 tsconfig.json配置详解
json
{
"compilerOptions": {
/* 基本选项 */
"target": "es2016", /* 指定 ECMAScript 目标版本 */
"module": "commonjs", /* 指定生成哪个模块系统代码 */
"lib": [], /* 指定要包含在编译中的库文件 */
"allowJs": true, /* 允许编译 javascript 文件 */
"checkJs": true, /* 报告 javascript 文件中的错误 */
"jsx": "preserve", /* 指定 jsx 代码用于的开发环境 */
"declaration": true, /* 生成相应的 '.d.ts' 文件 */
"sourceMap": true, /* 生成相应的 '.map' 文件 */
"outFile": "./", /* 将输出文件合并为一个文件 */
"outDir": "./dist", /* 指定输出目录 */
"rootDir": "./src", /* 用来控制输出目录结构 */
"removeComments": true, /* 删除编译后的所有的注释 */
/* 严格的类型检查选项 */
"strict": true, /* 启用所有严格类型检查选项 */
"noImplicitAny": true, /* 在表达式和声明上有隐含的 any类型时报错 */
"strictNullChecks": true, /* 启用严格的 null 检查 */
"strictFunctionTypes": true, /* 启用严格的函数类型检查 */
"strictBindCallApply": true, /* 启用严格的 bind, call, apply 检查 */
"strictPropertyInitialization": true, /* 启用类的属性初始化检查 */
"noImplicitThis": true, /* 当 this 表达式值为 any 类型的时候,生成一个错误 */
"alwaysStrict": true, /* 以严格模式检查每个模块,并在每个文件里加入 'use strict' */
/* 额外的检查 */
"noUnusedLocals": true, /* 有未使用的变量时,抛出错误 */
"noUnusedParameters": true, /* 有未使用的参数时,抛出错误 */
"noImplicitReturns": true, /* 并不是所有函数里的代码都有返回值时,抛出错误 */
"noFallthroughCasesInSwitch": true, /* 报告 switch 语句的 fallthrough 错误 */
/* 模块解析选项 */
"moduleResolution": "node", /* 选择模块解析策略 */
"baseUrl": "./", /* 解析非相对模块名的基准目录 */
"paths": {}, /* 模块名到基于 baseUrl 的路径映射的列表 */
"rootDirs": [], /* 根文件夹列表 */
"typeRoots": [], /* 包含类型定义的文件夹列表 */
"types": [], /* 需要包含的类型声明文件名列表 */
"allowSyntheticDefaultImports": true, /* 允许从没有设置默认导出的模块中默认导入 */
"esModuleInterop": true, /* 通过为所有导入创建命名空间对象,实现 CommonJS 和 ES 模块之间的互操作性 */
/* Source Map Options */
"sourceRoot": "", /* 指定调试器应该找到 TypeScript 文件而不是源文件的位置 */
"mapRoot": "", /* 指定调试器应该找到映射文件而不是生成文件的位置 */
"inlineSourceMap": true, /* 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件 */
"inlineSources": true, /* 将代码与 sourcemaps 生成到一个文件中 */
/* 其他选项 */
"experimentalDecorators": true, /* 启用实验性的ES装饰器 */
"emitDecoratorMetadata": true, /* 给源码里的装饰器声明加上设计类型元数据 */
"skipLibCheck": true, /* 跳过声明文件的类型检查 */
"forceConsistentCasingInFileNames": true /* 不允许不一致的引用同一文件的大小写 */
}
}11. TypeScript与现代前端框架
11.1 TypeScript与React
typescript
// React组件示例
import React, { useState, useEffect } from 'react';
interface User {
id: number;
name: string;
email: string;
}
interface UserListProps {
users: User[];
onSelectUser: (user: User) => void;
}
const UserList: React.FC<UserListProps> = ({ users, onSelectUser }) => {
const [selectedUser, setSelectedUser] = useState<User | null>(null);
useEffect(() => {
if (selectedUser) {
onSelectUser(selectedUser);
}
}, [selectedUser, onSelectUser]);
return (
<div>
{users.map(user => (
<div
key={user.id}
onClick={() => setSelectedUser(user)}
className={selectedUser?.id === user.id ? 'selected' : ''}
>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
))}
</div>
);
};
export default UserList;11.2 TypeScript与Vue
typescript
// Vue 3 Composition API 示例
import { defineComponent, ref, computed, PropType } from 'vue';
interface Todo {
id: number;
text: string;
completed: boolean;
}
export default defineComponent({
name: 'TodoList',
props: {
todos: {
type: Array as PropType<Todo[]>,
required: true
}
},
setup(props) {
const newTodo = ref('');
const filteredTodos = computed(() => {
return props.todos.filter(todo => !todo.completed);
});
const addTodo = () => {
if (newTodo.value.trim()) {
// 发送事件给父组件
emit('add-todo', newTodo.value);
newTodo.value = '';
}
};
return {
newTodo,
filteredTodos,
addTodo
};
}
});12. TypeScript最佳实践
12.1 使用严格模式
始终在tsconfig.json中启用严格模式:
json
{
"compilerOptions": {
"strict": true
}
}12.2 合理使用any类型
避免过度使用any类型,尽量使用具体的类型或泛型。
12.3 使用接口而非类型别名定义对象形状
typescript
// 推荐
interface User {
name: string;
age: number;
}
// 而不是
type User = {
name: string;
age: number;
};12.4 使用联合类型而非枚举
typescript
// 推荐
type Direction = 'north' | 'south' | 'east' | 'west';
// 而不是
enum Direction {
North,
South,
East,
West
}12.5 使用类型守卫
typescript
function isString(value: unknown): value is string {
return typeof value === 'string';
}
// 使用
if (isString(input)) {
// 在这里input被推断为string类型
console.log(input.toUpperCase());
}13. TypeScript工具和生态系统
13.1 TypeScript Playground
TypeScript官方提供的在线编辑器,可以快速测试TypeScript代码: https://www.typescriptlang.org/play
13.2 DefinitelyTyped
社区维护的类型定义库,为流行的JavaScript库提供类型定义:
bash
npm install --save-dev @types/node
npm install --save-dev @types/lodash13.3 TypeScript ESLint
结合ESLint进行代码质量检查:
bash
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin总结
TypeScript通过引入静态类型检查,极大地提高了JavaScript代码的质量和可维护性。通过本文的学习,你应该掌握了:
- TypeScript的基本类型和类型系统
- 接口和类的使用
- 泛型的概念和应用
- 高级类型特性如联合类型、交叉类型等
- 装饰器的使用
- 模块系统和命名空间
- TypeScript配置选项
- 与现代前端框架的集成
- 最佳实践和工具生态
在实际项目中,合理使用TypeScript可以显著减少运行时错误,提高代码可读性和团队协作效率。随着TypeScript生态的不断完善,它已成为现代前端开发的重要组成部分。