前端TypeScript高级技巧:让你的代码更安全

前端TypeScript高级技巧:让你的代码更安全

毒舌时刻

前端TypeScript?这不是增加工作量吗?

"JavaScript就够了,为什么要用TypeScript"------结果类型错误频发,调试困难,

"TypeScript太严格了,我写起来很麻烦"------结果代码质量差,维护困难,

"我只在关键地方用TypeScript,其他地方用any"------结果失去了TypeScript的意义。

醒醒吧,TypeScript不是负担,而是提高代码质量的利器!

为什么你需要这个?

  • 类型安全:在编译时发现类型错误
  • 代码提示:提供更好的IDE智能提示
  • 重构安全:重构代码时更加安全
  • 可读性:代码更加清晰易懂
  • 可维护性:减少运行时错误,提高代码可维护性

反面教材

typescript 复制代码
// 反面教材:过度使用any
function processData(data: any) {
  // 没有类型检查,容易出错
  return data.name.toUpperCase();
}

// 反面教材:类型定义不完整
interface User {
  id: number;
  name: string;
  // 缺少email等其他属性
}

// 反面教材:类型断言滥用
function getUser(id: number): User {
  // 不安全的类型断言
  return fetch(`/api/users/${id}`).then(res => res.json()) as unknown as User;
}

正确的做法

typescript 复制代码
// 正确的做法:使用泛型
function identity<T>(arg: T): T {
  return arg;
}

// 使用泛型约束
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

// 正确的做法:使用联合类型和类型守卫
type Shape = Circle | Square;

interface Circle {
  kind: 'circle';
  radius: number;
}

interface Square {
  kind: 'square';
  sideLength: number;
}

function getArea(shape: Shape): number {
  // 类型守卫
  if (shape.kind === 'circle') {
    return Math.PI * shape.radius ** 2;
  } else {
    return shape.sideLength ** 2;
  }
}

// 正确的做法:使用类型推断
const user = {
  id: 1,
  name: '张三',
  email: 'zhangsan@example.com'
};

// TypeScript会自动推断user的类型

// 正确的做法:使用映射类型
interface Person {
  name: string;
  age: number;
}

// 生成只读类型
type ReadonlyPerson = Readonly<Person>;

// 生成可选类型
type PartialPerson = Partial<Person>;

// 生成必填类型
type RequiredPerson = Required<PartialPerson>;

// 正确的做法:使用条件类型
// 提取Promise的返回类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// 测试
async function fetchData(): Promise<string> {
  return 'data';
}

// 类型会被推断为string
let data: UnwrapPromise<ReturnType<typeof fetchData>>;

// 正确的做法:使用模板字面量类型
type EventName<T extends string> = `${T}Changed`;
type MouseEventName = EventName<'click' | 'mouseover' | 'mouseout'>;
// 类型为 'clickChanged' | 'mouseoverChanged' | 'mouseoutChanged'

// 正确的做法:使用类型别名和接口
// 类型别名

type UserID = number;
type UserName = string;
type Email = string;

// 接口
interface User {
  id: UserID;
  name: UserName;
  email: Email;
  createdAt: Date;
  updatedAt: Date;
}

// 正确的做法:使用枚举
enum Role {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest'
}

function checkPermission(role: Role): boolean {
  return role === Role.Admin;
}

// 正确的做法:使用命名空间
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:
相关推荐
Lkstar13 小时前
Vue Router 进阶:导航守卫、动态路由与懒加载,源码级理解
前端
ricardo197313 小时前
# Tree Shaking 深度解析:为什么你的代码没被摇掉?
前端·面试
前端流一13 小时前
踩坑实录:Vite打包AntD5报错 rc-picker/es/generate/dayjs 模块找不到
前端
_按键伤人_13 小时前
三、手把手教你从零写一个本地 RAG
前端·llm·ai编程
008爬虫实战录13 小时前
【码上爬】 题十二:如来神掌 困难, JSVMP加密,使用代理补环境
前端·javascript·node.js
008爬虫实战录13 小时前
【码上爬】 题十:魔改算法 堆栈分析,找加密值过程详解
前端·python·算法
无人装备硬件开发爱好者13 小时前
深度解析GPS天线设计:从贴片天线到LNA前端的完整硬件方案
前端
Richown14 小时前
容器编排:Kubernetes高级调度策略
区块链·react
卷帘依旧14 小时前
React Hook采用环形链表的原因
前端
lichenyang45314 小时前
从 HarmonyOS AI 聊天模块理解工程化架构:MVVM、Controller、Provider、请求封装与 SSE
前端