TypeScript很麻烦,不想使用!

公众号:程序员白特,欢迎一起交流学习~

来源:前端大骆,https://juejin.cn/post/7344282440725577765

前言

最近,我们部门在开发一个组件库时,我注意到一些团队成员对使用TypeScript表示出了抵触情绪,他们常常抱怨说:“TypeScript太麻烦了,我们不想用!”起初,我对此感到困惑:TypeScript真的有那么麻烦吗?然而,当我抽时间审查队伍的代码时,我终于发现了问题所在。在这篇文章中,我想和大家分享我的一些发现和解决方案。

一、类型复用不足

在代码审查过程中,我发现了大量的重复类型定义,这显著降低了代码的复用性。

进一步交流后,我了解到许多团队成员并不清楚如何在TypeScript中复用类型。TypeScript允许我们使用typeinterface来定义类型。

当我询问他们typeinterface之间的区别时,大多数人都表示不清楚,这也就难怪他们不知道如何有效地复用类型了。

type定义的类型可以通过交叉类型(&)来进行复用,而interface定义的类型则可以通过继承(extends)来实现复用。值得注意的是,typeinterface定义的类型也可以互相复用。下面是一些简单的示例:

复用type定义的类型:

type Point = {  
  x: number;  
  y: number;  
};  
  
type Coordinate = Point & {  
  z: number;  
};  

复用interface定义的类型:

interface Point {  
  x: number;  
  y: number;  
};  
  
interface Coordinate extends Point {  
  z: number;  
}  

interface复用type定义的类型:

type Point = {  
  x: number;  
  y: number;  
};  
  
interface Coordinate extends Point {  
  z: number;  
}  

type复用interface定义的类型:

interface Point {  
  x: number;  
  y: number;  
};  
  
type Coordinate = Point & {  
  z: number;  
};  

二、复用时只会新增属性的定义

我还注意到,在类型复用时,团队成员往往只是简单地为已有类型新增属性,而忽略了更高效的复用方式。

例如,有一个已有的类型Props需要复用,但不需要其中的属性c。在这种情况下,团队成员会重新定义Props1,仅包含Props中的属性ab,同时添加新属性e

interface Props {  
  a: string;  
  b: string;  
  c: string;  
}  
  
interface Props1 {  
  a: string;  
  b: string;  
  e: string;  
}  

实际上,我们可以利用TypeScript提供的工具类型Omit来更高效地实现这种复用。

interface Props {  
  a: string;  
  b: string;  
  c: string;  
}  
  
interface Props1 extends Omit<Props, 'c'> {  
  e: string;  
}  

类似地,工具类型Pick也可以用于实现此类复用。

interface Props {  
  a: string;  
  b: string;  
  c: string;  
}  
  
interface Props1 extends Pick<Props, 'a' | 'b'> {  
  e: string;  
}  

OmitPick分别用于排除和选择类型中的属性,具体使用哪一个取决于具体需求。

三、未统一使用组件库的基础类型

在开发组件库时,我们经常面临相似功能组件属性命名不一致的问题,例如,用于表示组件是否显示的属性,可能会被命名为showopenvisible。这不仅影响了组件库的易用性,也降低了其可维护性。

为了解决这一问题,定义一套统一的基础类型至关重要。这套基础类型为组件库的开发提供了坚实的基础,确保了所有组件在命名上的一致性。

以表单控件为例,我们可以定义如下基础类型:

import { CSSProperties } from 'react';  
  
type Size = 'small' | 'middle' | 'large';  
  
type BaseProps<T> = {  
  /**  
   * 自定义样式类名  
   */  
  className?: string;  
  /**  
   * 自定义样式对象  
   */  
  style?: CSSProperties;  
  /**  
   * 控制组件是否显示  
   */  
  visible?: boolean;  
  /**  
   * 定义组件的大小,可选值为 small(小)、middle(中)或 large(大)  
   */  
  size?: Size;  
  /**  
   * 是否禁用组件  
   */  
  disabled?: boolean;  
  /**  
   * 组件是否为只读状态  
   */  
  readOnly?: boolean;  
  /**  
   * 组件的默认值  
   */  
  defaultValue?: T;  
  /**  
   * 组件的当前值  
   */  
  value?: T;  
  /**  
   * 当组件值变化时的回调函数  
   */  
  onChange: (value: T) => void;  
}  

基于这些基础类型,定义具体组件的属性类型变得简单而直接:

interface WInputProps extends BaseProps<string> {  
  /**  
   * 输入内容的最大长度  
   */  
  maxLength?: number;  
  /**  
   * 是否显示输入内容的计数  
   */  
  showCount?: boolean;  
}  

通过使用type关键字定义基础类型,我们可以避免类型被意外修改,进而增强代码的稳定性和可维护性。

四、处理含有不同类型元素的数组

在审查自定义Hook时,我发现团队成员倾向于返回对象,即使Hook只返回两个值。

虽然这样做并非错误,但它违背了自定义Hook的一个常见规范:当Hook返回两个值时,应使用数组返回。

团队成员解释说,他们不知道如何定义含有不同类型元素的数组,通常会选择使用any[],但这会带来类型安全问题,因此他们选择返回对象。

实际上,元组是处理这种情况的理想选择。通过元组,我们可以在一个数组中包含不同类型的元素,同时保持每个元素类型的明确性。

function useMyHook(): [string, number] {  
  return ['示例文本', 42];  
}  
  
function MyComponent() {  
  const [text, number] = useMyHook();  
  console.log(text);  // 输出字符串  
  console.log(number);  // 输出数字  
  return null;  
}  

在这个例子中,useMyHook函数返回一个明确类型的元组,包含一个string和一个number。在MyComponent组件中使用这个Hook时,我们可以通过解构赋值来获取这两个不同类型的值,同时保持类型安全。

五、处理参数数量和类型不固定的函数

审查团队成员封装的函数时,我发现当函数的参数数量不固定、类型不同或返回值类型不同时,他们倾向于使用any定义参数和返回值。

他们解释说,他们只知道如何定义参数数量固定、类型相同的函数,对于复杂情况则不知所措,而且不愿意将函数拆分为多个函数。

这正是函数重载发挥作用的场景。通过函数重载,我们可以在同一函数名下定义多个函数实现,根据不同的参数类型、数量或返回类型进行区分。

function greet(name: string): string;  
function greet(age: number): string;  
function greet(value: any): string {  
  if (typeof value === "string") {  
    return `Hello, ${value}`;  
  } else if (typeof value === "number") {  
    return `You are ${value} years old`;  
  }  
}  

在这个例子中,我们为greet函数提供了两种调用方式,使得函数使用更加灵活,同时保持类型安全。

对于箭头函数,虽然它们不直接支持函数重载,但我们可以通过定义函数签名的方式来实现类似的效果。

type GreetFunction = {  
  (name: string): string;  
  (age: number): string;  
};  
  
const greet: GreetFunction = (value: any): string => {  
  if (typeof value === "string") {  
    return `Hello, ${value}`;  
  } else if (typeof value === "number") {  
    return `You are ${value} years old.`;  
  }  
  return '';  
};  

这种方法利用了类型系统来提供编译时的类型检查,模拟了函数重载的效果。

六、组件属性定义:使用type还是interface

在审查代码时,我发现团队成员在定义组件属性时既使用type也使用interface

询问原因时,他们表示两者都可以用于定义组件属性,没有明显区别。

由于同名接口会自动合并,而同名类型别名会冲突,我推荐使用interface定义组件属性。这样,使用者可以通过declare module语句自由扩展组件属性,增强了代码的灵活性和可扩展性。

interface UserInfo {  
  name: string;  
}  
interface UserInfo {  
  age: number;  
}  
  
const userInfo: UserInfo = { name: "张三", age: 23 };  

结语

TypeScript的使用并不困难,关键在于理解和应用其提供的强大功能。如果你在使用TypeScript过程中遇到任何问题,不清楚应该使用哪种语法或技巧来解决,欢迎在评论区留言。我们一起探讨,共同解决TypeScript中遇到的挑战。

#前端##牛客在线求职答疑中心##我的实习求职记录##实习,投递多份简历没人回复怎么办##你们的毕业论文什么进度了#
全部评论

相关推荐

2025届&nbsp;文远知行&nbsp;系统研发工程师&nbsp;一面&nbsp;70min1.&nbsp;操作系统项目是怎么样的一个项目,是&nbsp;demo&nbsp;吗,代码量有多少2.&nbsp;ext4&nbsp;排查过程详细讨论,在排查的处理逻辑方面讨论了很久3.&nbsp;Linux&nbsp;系统中,有关于读写的调用有哪些,pread&nbsp;和&nbsp;pwrite&nbsp;了解吗4.&nbsp;如果一个硬盘&nbsp;IO&nbsp;时阻塞了,会发生什么情况5.&nbsp;一个&nbsp;read&nbsp;请求的全流程,从用户态接口到落到物理磁盘6.&nbsp;netstat?7.&nbsp;查看&nbsp;CPU&nbsp;占用的情况,里面有个&nbsp;wait&nbsp;字段是什么意思8.&nbsp;如何查看硬盘的占用?du&nbsp;df&nbsp;-h9.&nbsp;做题:给定一个不含有重复值的数组&nbsp;arr,找到每一个&nbsp;i&nbsp;位置左边和右边离&nbsp;i&nbsp;位置最近且值比&nbsp;arr[i]&nbsp;小的位置。返回所有位置相应的信息。(25届校招已开放,26届实习生同步招聘)💼&nbsp;公司岗位:全球领先的L4级自动驾驶技术公司,目前在招岗位涵盖算法,开发,测试,硬件相关岗位,base北上广深/美国圣何塞/新加坡;🫶️&nbsp;团队氛围:工作不用打卡,环境开放,氛围良好,大牛多多,公司每月开一次全员会议,更新最新公司进展与解答员工关心的问题;👨🏻‍💻&nbsp;工作环境:总部在广州海珠国际生物岛,窗外风景很好,办公区还有台球桌和乒乓球桌,楼下也适合散步跑步打球等各种户外活动;💰&nbsp;薪资待遇:岗位不同待遇不同,研发人员全员持股,六险一金齐全,公积金最高档缴纳;公司全年包三餐,每周可以自选工作日每天的餐品,每天有免费零食,夏天有备冰淇淋雪糕等;❤️&nbsp;职场感受:个人感觉是很适合历练成长的地方,在这里只要有付出,不愁得不到回报,能力突出者很快能有独当一面的机会;【内推链接】https://app.mokahr.com/m/campus_apply/jingchi/2137?recommendCode=DSaUuUtT#/jobs【内推码】DSaUuUtT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;【备注】使用内推码的同学可以留下【姓名缩写+岗位】帮你跟进进度哦&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#文远知行#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#内推#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#内推码#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#秋招#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#实习#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
点赞 评论 收藏
分享
03-30 01:16
已编辑
湖南科技大学 前端工程师
1.&nbsp;封装组件的话有用过插槽吗,作用域插槽呢2.&nbsp;父子之间的传值是怎么传的,父子通信3.&nbsp;provide和inject用过吗,接受什么类型的数据,能接收函数吗(能)4.&nbsp;vue2中父与子生命周期的执行顺序5.&nbsp;组件中的name和路由的name有什么作用6.&nbsp;路由传参有params和query,params是怎么传值的(router中的name就是配合params进行路由跳转和获取动态的参数,而query是通过push路径的方式进行传值)7.&nbsp;vue中的keep-alive用过这个组件吗,不是HTTP中的8.&nbsp;场景:没有在data函数中去定义,我是直接在mounted函数中直接用this.a等于1,然后我把这个a绑定在这个模板上,此时呢有一个按钮是改变这个a的值,把this.a变成等于2,那么这个模板中的a显示的值是1还是29.&nbsp;vue2响应式是怎么处理的10.&nbsp;ref和reactive的区别,分别去解构还会是响应式吗(reactive失去响应式,ref不会)11.&nbsp;watch和watchEffect用到过吗12.&nbsp;html中script标签设置defer和async有什么用13.&nbsp;cookie,localStorage,sessionStorage区别,两个标签页的sessionStorage能共享吗(不能),localStorage呢(能)14.&nbsp;vuex和pinia在两个标签页中能共享吗(不能)15.&nbsp;二级域名下的cookie在一级域名下能拿到吗(不能),反过来可以,向下兼容16.&nbsp;盒模型,content-box标准盒模型(它不包含margin,你实测过吗)17.&nbsp;两栏布局,左边固定,右边自适应怎么实现(flex)18.&nbsp;水平垂直居中布局19.&nbsp;外层div设置了transform,内层设z-index=999生效吗20.&nbsp;行内元素的margin和padding左右方向生效,上下不生效21.&nbsp;map和forEach的区别,可以链式写吗(链式的前提是有返回值,map可以写,因为有返回值)22.&nbsp;git的命令,删除本地的分支(git&nbsp;branch&nbsp;-d&nbsp;),git&nbsp;pull和git&nbsp;pull&nbsp;--rebase的区别#前端凉经#
查看22道真题和解析
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务