简介
简单了解了TypeScript的背景之后,本文将用最简单快速的方式,为您揭开TypeScript(以下简称ts)的神秘面纱,直接上手参与ts项目!
阅读人群:熟悉JavaScript语言,但对TypeScript不了解、没有实践过的前端研发人员。
一切皆有类型!
与JavaScript相比,ts引入了类型系统,通过类型注解提供编译时的静态类型检查,也就是说,ts将会在编译阶段就对所有数据进行类型的合法检查,对潜在的类型错误抛出异常,而不会等到项目部署上线,导致线上事故!
通过ts的类型系统,我们的应用程序将会更加规范、稳健,对于大型的复杂应用,也将更利于扩展和维护。
什么是类型系统?
在计算机的世界当中,一切数据都有类型,数据在我们的代码中对应的是变量、常量,也就是说:变量、常量都要有类型!
一个类型决定了一个变量具有哪些属性和方法,访问某种类型不具有的属性或方法将导致程序报错。
举个例子,我们在js当中这样定义和使用变量:
var a = 1
a = 'hello'
a = false
- 以上代码定了一个变量 a,它的初始值是数字 1,那么它的数据类型就是数值类型。
- 然而,代码第二行,我们给 a 重新赋值了字符串 hello,此时它的数据类型变成了字符串类型!
- 代码第三行,我们给 a 再次赋值布尔值
false
,现在它的数据类型又成了布尔类型。
这是因为:JavaScript是一种动态类型的脚本语言,同一个变量可以用作不同的数据类型。这在实际开发中虽然很灵活,但同时也会带来灾难!
当我们访问一个变量的属性和方法时,代码编辑器并不会立刻检查它的数据类型,因为只有在运行时,它的类型才能确定!
试想一下,在你的项目工程中,你对外提供了一个函数,它接收一个字符串类型的参数,在函数内部,你会获取这个参数的字符个数,然而当调用者给你误传了一个数值类型的参数,程序将会崩溃!(这个错误只会在运行时发生)
function getStringLength (str) {
var len = str.length;
return len;
}
let a = 6;
getStringLength(a); // error: 数值类型的a不存在length属性!
因此,ts很好的解决了这一问题,因为:一切皆有类型!
还是上面的例子,看一下ts是如何实现的,先不用在意其中的语法。
function getStringLength (str: string):number {
var len:number = str.length;
return len;
}
let a:number = 6;
const count: number = getStringLength(a); // error: 无法通过编译!
以上代码会在你的编辑器中直接报错,无法编译!在开发阶段,ts就已经预知了潜在的错误,这就是类型系统的伟大之处!
让我们来分析这段代码:
- 首先看函数的入参 str,它的后面有个冒号,冒号后面是
string
,这在ts中表示,变量 str 是字符串类型的!这意味着:str只接受字符串类型的值,其他类型都不允许! - 函数
getStringLength
的参数括弧后面有个冒号和number
,这在ts中表示,此函数将返回一个number
类型的数据,如果函数不返回任何数据,就用void
表示。 - 代码最后两行,定义了一个
number
类型的 a,并将 a 作为参数调用函数getStringLength
,此时编译会报错!原因是函数getStringLength
的入参必须是 string 类型!
如此可见,在ts中,所有的一切都需要明确的数据类型,无论是函数入参、变量、常量、还是函数返回值等。定义类型的语法是冒号后面跟上数据类型。
基础数据类型
ts提供了以下基础数据类型,定义变量类型的语法是:冒号后面写上类型,如:let userName: string;
类型 | 语法关键字 | 字符串 | string | 数值 | number | 布尔值 | boolean | 空值 | void | 任意值 | any | undefined | undefined | null | null |
---|
注意:所有数据类型的关键字都是小写开头的!
任意值 any
虽然ts提供了任意值类型 any
,它用来表示在编程阶段无法明确类型的数据,但在实际开发中,我们要尽量避免去使用它!因为它的问题同js一样,那就是在编译阶段无法进行类型检查。
看下面这个示例:
let a:any = 1;
let count:number = a.length; // error: 运行时报错,数值类型不存在length属性
上面这段代码在编辑器中不会报错,因为 a 的数据类型是any
,这意味着它可能是任意类型的,因此类型检查系统无法明确它到底有哪些属性或方法。
联合类型
在实际开发中,我们通常不能确定一个变量到底是什么类型,但可以预知它是某几种类型中的一种,比如:用来接收网络接口返回的状态码的变量 code,它可能是数值、也可能是字符串。
这种情况就要用到联合类型了,代码如下:
let code: number | string;
code = 0
code = '0'
code = false // error: 联合类型code只能是string或者number,不能是boolean
- 如上代码所示,多个类型之间用竖线分隔,表示这个变量是一个联合类型。
- 联合类型的变量只能赋值为定义好的几种类型,而不能赋值未定义的类型,如上示例,code只能赋值字符串或者数值,但不能赋值布尔值!
- 一旦为联合类型赋值,那就只能访问赋值类型的属性或方法,比如示例中的code,如果赋值为0,那就不能访问
code.length
,因为此时code已明确为数值类型了。
类型断言
看下面这个示例,当你为一个函数的入参定义了联合类型,在你的代码中,你可能需要获取某种类型的属性,此时需要用到类型断言。
类型断言的语法是 as
,如:val as string
function checkData (code: string | number): boolean {
if (code as string) {
return (code as string) === '0'
} else {
return (code as number) === 0
}
}
类型断言的意思是告诉编译器,将这个变量作为某种类型去检查。注意:类型断言只能是联合类型中定义的类型。
接口
接口是对一个键值对结构(js中的Object
类型)数据特征的描述,我们在js中可以使用 {}
来定义任意结构的数据,但它也带来了很大的风险,如下示例:
let user = {
name: 'zhangsan',
age: 33,
school: 'bei jing da xue'
}
user.info.message // error: 运行时报错!因为user对象不存在info属性
user.age.length // error: 运行时报错!因为user对象的age属性是数值类型,不存在length属性
像上面这种错误,占据了前端线上问题的很大比例,因为在js中的对象,我们在访问它的属性或方法时,完全是无法预知的,只能依赖调用者传递正确的数据,这大大降低了应用程序的建壮性。
让我来看看在ts中如何解决这个问题!
接口的定义
通过下面的代码,我们定义了一个User接口,我建议大家将接口理解为自定义类型,也就是说,除了ts提供的基础数据类型外,我们可以自定义具有复杂结构的数据类型。
interface User {
name: string;
age: number;
isAdmin: boolean;
}
接口定义语法:interface
<接口名> { [属性名]: <属性类型>; }
注意:接口定义规范要求,所有接口名称首字母应大写。(在Java语言中,任意接口的首字母都是 I 开头的,但在ts中,笔者不建议这么做。)
如代码所示,接口User
规定了一种数据结构的特征,我们把它理解为一种数据类型的话,所有User
类型的数据,都要具有以下要求:
- 必须有name属性且必须是字符串类型;
- 必须有age属性且必须是数值类型;
- 必须有isAdmin属性且必须是布尔值类型;
- 除了以上三个属性,不能再出现其他属性!
有了以上约束,我们就可以非常方便的定义和使用这样“严谨”的数据了。
接口的使用
通过下面的代码,我们定义了一个User
类型的变量并给它赋值:
let user: User = {
name: 'zhangsan',
age: 33,
isAdmin: false
};
user.age = 32;
user.name = 6; // error-name必须是字符串类型
user.school = 'bei da'; // error-User接口不存在school属性
- 可以看到,我们定义的变量user冒号后面写着
User
,这表示变量user是一个User
类型的数据,那么它必须符合User所规定的特征!也就是必须含有name、age和isAdmin属性, - 并且每个属性的数据类型也必须符合
User
的规定。如果任意一个特征不符合,编译器就会报错!
有了这样“强制”的约束,我们就可以定义一些自己需要的数据接口(自定义数据类型),这样就可以在程序开发阶段,确保使用者安全的传递数据了。
可选属性
通过上文我们得知,接口规定了一组键值对数据结构的特征,我们使用某个接口定义变量并赋值时必须含有所有属性,但实际开发中,有些数据的属性可能并不是必须的,可有可无。
在ts中,通过可选属性就可以实现这种需求,看如下代码:
interface User {
name: string;
age: number;
isAdmin: boolean;
school?: string; // 这是一个可选属性
}
let user: User = {
name: 'zhangsan',
age: 33,
isAdmin: false
} // 程序不会报错,因为school是可选属性,可以不赋值。
如上所示,属性school的后面有一个问号,这表示,User
这种数据类型在赋值时,school属性是可有可无的。
任意属性
还有一种情况,我们在定义接口的时候,除了已知的属性列表,可能并不确定实际的数据还有哪些属性,这样我们并不能严格的定义这种数据特征。
因此,ts提供了任意属性的定义,如下代码所示:
interface User {
name: string;
age: number;
isAdmin: boolean;
school?: string;
[propName: string]: any; // 这是一个任意属性
}
let user: User = {
name: 'zhangsan',
age: 33,
isAdmin: false,
sex: 1
} // 程序不会报错,因为User含有任意属性,因此赋值sex属性没问题。
任意属性的语法:[propName: string]: any,注意:任意属性的数据类型必须是 any!
其他
(数组、函数、枚举、泛型,将在后续的文章中持续更新,敬请期待!)
掘金的第一篇文章,请大家多多支持!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!