相信很多团队遇到过,接入了TS,但实际开发中,很多类型用的是any,这种情况导致了接了ts不仅没有产生积极效果,反而增加了开发成本(毕竟写个any也是三个字符,还有一堆其他接入TS的成本) 那么为什么我们要用ts?为什么用完之后还要写那么多的any呢?
typescript总是被用作“anyscript”?
在很多开发的项目中经常出现一堆的“any”,有时候开发同学为了图方便,所以出现了很多的any。毕竟要去定义一个类型 远比写一个any 来的麻烦。然后我在项目中搜了一下,出现了436个any。如果罚抄100遍算惩罚的话,这个项目已经惩罚我4.36遍了。
如何解决这种现象呢?
为了解决这个问题我还特地去百度了下,想要寻求答案,然后我打开百度
解铃还须系铃人
发现问题不少但是答案不多,于是乎,我在茫茫答案中在一个角落里,找到一条没被别人点赞,不起眼的答案,解铃还须系铃人。
谁写的,让谁不写就完了?说吧,我望向在座的各位,大呼一声,刀呢?谁TM老写any我剁了谁?这时候我耳边飘来一个弱弱的声音,兄弟,我看了一下git提交记录,上面显示是你写的。。。 好嘛~有话好好说,硬来不行,咱们就聊聊为什么我们最好不用any,为什么要去定义一个类型呢?
为什么我要去定义一个类型?
首先我们要明白我们为什么要去定义一个类型,它有啥用?
TypeScript 发展至今,已经成为大型项目的标配,其提供的静态类型系统,大大增强了代码的可读性以及可维护性;同时,它提供最新和不断发展的 JavaScript 特性,能让我们建立更健壮的组件。
首先是为了代码补全
首先我们定义num为number属性之后我们就获得了number类型上面的一些方法,进行补全了。
第二就是类型检查
类型检查就很关键了,比如我们将string类型赋值给number类型之后就会报错。
增加了代码的可读性和可维护性
类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了 可以在编译阶段就发现大部分错误,这总比在运行时候出错好 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等 比如我们angular用的一个阿里的框架delon,有时候都不需要看文档,直接跳转定义,为了防止你们胡作非为,都做了类型限制,这在之前我想都不敢想的,遇到一些第三方要传参的东西,要么查文档,要么读源码,很是影响效率。
鼠标放上去直接有提示
直接跳转定义,每个值的含义,限制清清楚楚 那么既然这个玩意儿这么好,我们就来讲讲这个东西怎么写,怎么用?
大树底下好乘凉
牛顿的成功是因为他站在巨人的肩膀上,才成功的。我们充其量是大树底下好乘凉,我们在项目中引用第三方的不是ts的npm包的时候是没有类型定义文件的。我们可以通过npm下载第三方写好的定义文件库,来解决引入非ts的第三方项目的问题。 毫无疑问,DefinitelyTyped 是 TypeScript 最大的优势之一,社区已经记录了 90% 的顶级 JavaScript 库。
你可以通过 npm 来安装使用 @types,例如为 jquery 添加声明文件:
npm install @types/jquery --save-dev
自己动手丰衣足食
当然别的东西固然好用,还是得自己动手丰衣足食。那么我们如何去写一个声明文件。
javascript数据类型
- 原始数据类型
- Object MDN 文档地址
let isDone: boolean = false
let age: number = 10
// string
let firstName: string = 'ddking'
let message: string = `Hello, ${firstName}, age is ${age}`
// undefined null
let u: undefined = undefined
let n: null = null
// 注意 undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:
let num: number = undefined
// any
let notSure: any = 4
notSure = 'maybe it is a string'
notSure = 'boolean'
notSure.myName
notSure.getName()
那么什么是原始数据类型呢?MDN 的文档写的很清楚,除 Object 以外的所有类型都是不可变的(值本身无法被改变)。我们称这些类型的值为“原始值”。
Array 和 Tuple
说完原始数据类型,来说Object 类型,第一个要接触的就是数组,应该是我们前端开发工程师最熟悉的一种数据结构,它有多种定义方法,这里我们先简介最简单的一种。好,来到代码:
//最简单的方法是使用「类型 + 方括号」来表示数组:
let arrOfNumbers: number[] = [1, 2, 3, 4]
//数组的项中不允许出现其他的类型:
//数组的一些方法的参数也会根据数组在定义时约定的类型进行限制:
arrOfNumbers.push(3)
arrOfNumbers.push('abc')
函数
接下来说一说函数,函数是构成程序的重要组成部分
那么函数主要是由两部分构成的,一个是输入,一般是通过不同参数的传参来实现,第二个是输出,就是函数的返回结果。
function add(x: number, y: number): number {
return x + y
}
let result = add(2, 3)
多余或者少于这个参数是不可以的,会出现错误。
那么我们怎样实现可选参数呢?加一个问好就可以了
function add(x: number, y: number, z?: number): number {
if (typeof z === 'number') {
return x + y + z
} else {
return x + y
}
}
接口:interface
interface 可以帮我们非常方便的定义对象的类型,那这个概念就称之为 interface 接口,typescript 中的interface 非常的灵活,听到新概念大家先不要觉得它难以理解,我们来讲它的第一个主要功能:就是对对象的形状 shape 进行描述,这也是最容易理解的,interface 本身就像一种抽象的契约或者图纸,它非常灵活,可以描述编程语言的各种类型,总体来说是非常有意思的一个概念
interface Person {
name: string;
age: number;
}
// 接着定义了一个变量 ddking,它的类型是 Person。这样,我们就约束了 ddking 的形状必须和接口 Person 一致。
let ddking: Person ={
name: 'ddking',
age: 20
}
可选属性
interface Person {
name: string;
age?: number;
}
let ddking: Person = {
name: 'ddking'
};
只读属性
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
ddking.id = 9527;
interface 描述函数
interface ISum {
(x: number, y: number) : number
}
泛型的使用及其实现
了解范型,可以先思考一下范型是怎么出现的。
范型的动机
如上的图片我们传入和返回没办法做到统一这时候我们发现了一个问题,我们传入了字符串,但是返回了 any,我们的变量就丧失了类型,这可不是一个好现象,但是我们传入的类型是多种多样的,什么都可以,字符串,数字,布尔,甚至是复杂类型。这样传入和返回没法做到统一,甚至还可能出这么一个 bug
从我们之前讲的,类型推论,假如给一个变量赋值,ts 是可以帮你猜测到它的类型的,但是到了函数,我们发现它猜不了了,这就是 泛型诞生的 第一个原因,类型推断没法流动到函数里面去。但是我们还是想根据传入参数的不同返回对应的类型。 ts 其实所有的思想就是获得类型的,没有正确的类型,那么它的威力就少了百分之九十,这就是泛型出现的第一个原因
范型的定义
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
function echo<T>(arg: T): T {
return arg
}
可以参考这篇文章,讲的通俗易懂
TS 一些工具泛型的使用及其实现
这些泛型接口定义大多数是语法糖(简写), 甚至你可以在 typescript 包中的 lib.d.ts 中找到它的定义, 最新版的 typescript (2.9) 已经包含了大部分