面试题答案
一键面试架构层面
- 采用分层架构:
- 优势:将不同功能分层,如分为表现层、应用层、领域层、基础设施层。各层职责明确,降低模块间的耦合度,使得类型推断更清晰。例如,表现层专注于处理用户请求和响应,应用层负责业务流程编排,领域层包含核心业务逻辑,基础设施层处理外部资源交互。
- 实现:在TypeScript中,通过不同的文件目录结构来区分层次,如
src/presentation
、src/application
等。并且各层之间遵循单向依赖原则,比如表现层依赖应用层,应用层依赖领域层和基础设施层等。
- 使用依赖注入(DI)容器:
- 优势:通过依赖注入,将模块间的依赖关系集中管理。这样在实例化模块时,可以明确传入依赖的类型,减少类型推断的不确定性。
- 实现:可以使用InversifyJS这样的库。首先定义好接口和实现类,例如:
// 定义接口
interface UserService {
getUserById(id: number): Promise<any>;
}
// 实现类
class UserServiceImpl implements UserService {
async getUserById(id: number): Promise<any> {
// 实际逻辑
return { id, name: 'user' };
}
}
// 使用InversifyJS进行依赖注入
import { Container } from 'inversify';
const container = new Container();
container.bind<UserService>('UserService').to(UserServiceImpl);
在其他模块中使用时:
import { container } from './diConfig';
const userService = container.get<UserService>('UserService');
模块设计层面
- 明确模块接口:
- 优势:每个模块提供明确的接口,其他模块通过接口与该模块交互。这样在跨模块传递数据时,类型就由接口定义,减少类型推断问题。
- 实现:在TypeScript中,使用
export interface
来定义模块接口。例如,一个product
模块:
// product.ts
export interface Product {
id: number;
name: string;
price: number;
}
export interface ProductService {
getProductById(id: number): Promise<Product>;
}
- 减少模块间的直接依赖:
- 优势:如果模块A直接依赖模块B、C、D等多个模块,会使依赖关系复杂,类型推断困难。通过中间层或共享模块来管理依赖,可以简化依赖关系,利于类型推断。
- 实现:比如有模块A、B、C,模块A需要B和C的数据。可以创建一个共享模块
SharedData
,B和C将数据提供给SharedData
,A从SharedData
获取数据。在TypeScript中,SharedData
模块可以定义好数据的类型和获取方法。
代码实现层面
- 使用类型别名和泛型:
- 类型别名:
- 优势:对于复杂的数据结构或函数类型,可以使用类型别名来使其更清晰,方便在跨模块传递数据时类型推断。
- 实现:例如,定义一个表示用户信息的复杂类型:
- 类型别名:
type UserInfo = {
id: number;
name: string;
email: string;
address: {
street: string;
city: string;
};
};
在其他模块传递该类型数据时,直接使用UserInfo
类型,类型推断更明确。
- 泛型:
- 优势:在函数或类中使用泛型,可以提高代码的复用性,同时保证类型安全,在跨模块传递不同类型数据时很有用。
- 实现:例如,一个通用的获取数据的函数:
async function getData<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json() as Promise<T>;
}
在不同模块中调用时,可以传入具体的类型,如getData<Product>('/api/products')
。
2. 使用严格的类型检查:
- 优势:开启TypeScript的严格类型检查选项,如
strict: true
,可以让编译器在编译阶段发现更多类型错误,减少运行时因类型推断问题导致的代码混乱。 - 实现:在
tsconfig.json
文件中设置:
{
"compilerOptions": {
"strict": true
}
}
- 添加类型注释:
- 优势:在函数参数、返回值、变量声明等地方添加明确的类型注释,使类型推断更准确,方便其他开发人员理解代码。
- 实现:例如:
function addNumbers(a: number, b: number): number {
return a + b;
}
let num: number = 10;