MST
星途 面试题库

面试题:TypeScript全栈类型安全架构设计相关的复杂类型推断

假设你正在设计一个全栈的TypeScript应用,后端使用Node.js和Express,前端使用React。在这个应用中有一个数据传输对象(DTO),它需要在前后端共享。前端通过表单收集数据,后端对数据进行验证和处理。请描述如何使用TypeScript的类型系统来实现整个流程的类型安全,包括如何利用类型推断、交叉类型、联合类型等高级特性确保数据在前后端传输和处理过程中的类型一致性,并且给出关键代码示例及详细的设计思路。
28.7万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 共享DTO类型定义:在一个共享的TypeScript文件中定义DTO类型,确保前后端使用相同的类型定义。
  2. 前端类型安全:在React组件中使用这些类型定义来处理表单数据,利用类型推断简化类型声明。
  3. 后端类型安全:在Node.js和Express应用中使用相同的DTO类型定义进行数据验证和处理,通过联合类型处理可能的错误情况。
  4. 交叉类型:可以用于合并多个类型的属性,比如在后端验证成功后返回的对象可能会合并了原始DTO和一些附加信息。

关键代码示例

1. 共享DTO类型定义

在一个共享的types.ts文件中定义DTO类型:

// types.ts
export interface UserDTO {
  username: string;
  email: string;
  age: number;
}

2. 前端React部分

import React, { useState } from'react';
import { UserDTO } from './types';

const UserForm: React.FC = () => {
  const [userData, setUserData] = useState<UserDTO>({
    username: '',
    email: '',
    age: 0
  });

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    if (name === 'age') {
      setUserData({...userData, [name]: parseInt(value) });
    } else {
      setUserData({...userData, [name]: value });
    }
  };

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    // 这里可以将userData发送到后端
    console.log('Submitting data:', userData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="username" onChange={handleChange} placeholder="Username" />
      <input type="email" name="email" onChange={handleChange} placeholder="Email" />
      <input type="number" name="age" onChange={handleChange} placeholder="Age" />
      <button type="submit">Submit</button>
    </form>
  );
};

export default UserForm;

3. 后端Node.js和Express部分

import express from 'express';
import { UserDTO } from './types';

const app = express();
app.use(express.json());

app.post('/submit', (req, res) => {
  const userData: UserDTO | null = req.body;

  if (!userData || typeof userData.username!=='string' || typeof userData.email!=='string' || typeof userData.age!== 'number') {
    return res.status(400).json({ error: 'Invalid data' });
  }

  // 数据验证通过,进行处理
  console.log('Received data:', userData);
  res.json({ message: 'Data received successfully' });
});

const port = 3000;
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

在上述代码中,通过共享UserDTO类型定义,前端在表单处理中确保输入的数据类型符合要求,后端在接收数据时进行类型验证,利用类型推断简化代码编写,同时通过联合类型处理可能的错误情况。如果需要合并类型,比如后端处理后返回的数据类型,可以使用交叉类型,例如:

// 假设后端处理后返回的数据类型
export interface ProcessedUserDTO extends UserDTO {
  processedAt: string;
}

这样就可以在需要的地方使用ProcessedUserDTO类型,它包含了UserDTO的所有属性以及新的processedAt属性。