TypeScript使用经验总结

30 阅读5分钟

最近在把负责的项目从js转成ts,一些经验记录,因为这个项目是新搭的,只有七八个路由页,总共十来个组件,改起来难度比较小(当然主要是我第一次自己改,自己搞就容易一切从简,只能说尽力做到除了接口泛型不用any)

工厂函数

工厂函数(Factory Function)  是一种「不依赖 new 关键字,通过普通函数封装对象 / 数据 / 实例的创建逻辑,最终返回目标结果」的设计模式。

在项目中我遇到最多的工厂模式的场景就是组件封装的时候使用的defineProps

interface PropsAction{
    cc:string,
    dd:number
}

const props = withDefaults(defineProps<{
    aaa:string,
    bbb:PropsAction
}>(),{
    aaa:'',
    bbb:()=>({
        cc:''
        dd:0
    })
})

工厂模式整体上就是通过函数封装一个使用对象,来自豆包的例子

const createReactiveList = <T = DataType>(
  defaultData: T[] = [] // 可选默认值,支持泛型(灵活适配不同类型)
): Ref<T[]> => {
  // 内部封装:初始化 + 类型约束
  const list = ref<T[]>(defaultData);

  // 可额外封装操作逻辑(如添加、清空,按需扩展)
  const addItem = (item: T) => {
    list.value.push(item);
  };

  const clearList = () => {
    list.value = [];
  };

  // 返回响应式数据 + 操作方法(或仅返回数据)
  return {
    list,
    addItem,
    clearList,
  };
};

unknown和any的区别

unknown是ts的推荐+强安全性类型,any是不推荐的+无校验类型

unknown自带类型收缩,在真实的使用场景需要做类型校验才能够使用

const queryValue: unknown = route.query.aaa;

queryValue.toUpperCase();//会报错,无法使用,提示必须做类型检验
queryValue =  queryValue + 1;//会报错,无法使用,提示必须做类型检验

// 🌟 类型收窄后,TS 允许使用(和你之前的查询参数处理逻辑一致)
if (typeof queryValue === 'string') {
  queryValue.toUpperCase(); // ✅ 确认是 string 后,可安全调用方法
}

if (typeof queryValue === 'number') {
  queryValue + 1; // ✅ 确认是 number 后,可安全运算
}

const queryValue2: any = route.query.aaa;

queryValue2.toUpperCase();//会正常通过不会报错提示
queryValue2 =  queryValue2 + 1;//会正常通过不会报错提示

any一般应用的场景都在:

  1. 老项目迁移过渡用,方便快速转成ts,再支持后续迭代
  2. 第三方项目,不太清楚实际的内部结构,比如这次我的项目接的finebi平台,不知道一部分帆软的实际接口内部的类型定义,只能靠猜测一部分,一部分<T=any>去处理类型

类型守卫

本质上是在多种类型type的情况下,对于部分类型属性不兼容,导致需要收缩判断,去执行部分逻辑。

说人话就是:已知A数据一定是AAA|BBB|CCC中的一种,但不知道具体是哪一种,于是通过特定的条件判断,去确定在场景一A一定是AAA类型,场景二一定是BBB类型的函数或者语句。

interface AAA{
    aaa:number,
    ttt:number,
}
interface BBB{
    bbb:number,
    ttt:number,
}
interface CCC{
    ccc:number,
    ttt:number,
}

type TYPE = AAA|BBB|CCC

const isString = (x: unknown): x is string => {
  return typeof x === 'string';
};

const isAAA = (A:TYPE): A is AAA=>{
    return !!A && 'aaa' in A
}

const isAllowedIconKey = (x: string): x is VIconsKey => {
  return ['HomeOutline', 'OptionsOutline', 'UserOutline'].includes(x);
};

keyof typeof

keyof 是 TypeScript 特有的操作符,作用是提取一个类型的所有键名,返回这些键名组成的字符串 / 数字联合类型

typeof 是判断值的类型(返回 "string"/"number" 等),而 TypeScript 中的 typeof 是获取一个值的完整类型,需要绑定as const做一个类型约束

当你需要提取类的key作为type约束

export const vIcons = {
  NewspaperOutline: NewspaperOutline,
  FolderOutline: FolderOutline,
  OptionsOutline: OptionsOutline,
} as const; // 一定需要加as const 做一个类型约束

// 🌟 提取 vIcons 的键名类型(核心语法)
type VIconsKey = keyof typeof vIcons; 
//本质上上面等于下面
type VIconsKey = 'NewspaperOutline'| 'FolderOutline' | 'OptionsOutline'

never

常用于错误兜底的场景,比如非正常的枚举分支/try catch错误捕获后的分支处理,提高项目健壮性

enum MenuStatus {
  Normal = 'normal',
  Disabled = 'disabled',
  Hidden = 'hidden'
}

// 🌟 核心:兜底函数,接收 never 类型(只能传入不可能的类型)
function assertNever(value: never): never {
  throw new Error(`未处理的类型:${value}`);
}

// 处理菜单状态(确保覆盖所有枚举值)
function handleMenuStatus(status: MenuStatus): string {
  switch (status) {
    case MenuStatus.Normal:
      return '正常显示';
    case MenuStatus.Disabled:
      return '禁用状态';
    case MenuStatus.Hidden:
      return '隐藏状态';
    default:
      return assertNever(status);
  }
}

type,interface和元组

  • type: 给任意类型起别名,复用复杂类型
//联合类型
type abc = string|number
//交叉类型
type abc2 = BaseMenu & Menu
//元组
type abc3 = [number, string, number]
  • interface:常用于接口入参,支持extends扩展和implements实现
// 示例1:基础接口(菜单配置,项目中最常用)
interface Menu {
  label: string;
  path: string;
  icon?: string;
  children?: Menu[]; // 嵌套自身,描述子菜单
}

// 示例2:接口扩展(继承其他接口,比type交叉类型更语义化)
interface MenuWithAuth extends Menu {
  permissions: string[]; // 新增权限字段
}
const menu: MenuWithAuth = {
  label: '首页',
  path: '/dashboard',
  permissions: ['read', 'write'],
};

// 示例3:类实现接口(面向对象场景)
interface Printable {
  print: () => void;
}
class MenuService implements Printable {
  print() {
    console.log('打印菜单配置');
  }
}

Typescript的数据类型

  1. 基础数据类型

string,number,undefined,null,symbol,boolean,bigint

  1. 复合类型

array,tuple,object,enum

tuple是特殊的,固定长度固定类型的数组,比如 const arr:[number,string,number] = [1,'2',3]

enum是命名常量,常用可枚举的类型限制

  1. 特殊类型

any,unknown,never,void

any 不安全类型

unknown推荐的类型限制,不清楚具体类型,于是在实际的代码执行层再进行类型限制

never 永远不会执行的类型,一般用于错误捕获

void 函数无返回值

  1. 高级类型

联合类型,交叉类型,类型断言

联合类型: let str:number | number

交叉类型: type strname = Name & Age

类型断言: let val: unknown = "hello";const str = val as string;