TypeScript快速入门

484 阅读10分钟

简介

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。 TypeScript 由微软开发的自由和开源的编程语言。 TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。

简单了解了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就已经预知了潜在的错误,这就是类型系统的伟大之处!

让我们来分析这段代码:

  1. 首先看函数的入参 str,它的后面有个冒号,冒号后面是 string,这在ts中表示,变量 str 是字符串类型的!这意味着:str只接受字符串类型的值,其他类型都不允许!
  2. 函数 getStringLength 的参数括弧后面有个冒号和 number,这在ts中表示,此函数将返回一个number类型的数据,如果函数不返回任何数据,就用 void 表示。
  3. 代码最后两行,定义了一个number类型的 a,并将 a 作为参数调用函数 getStringLength,此时编译会报错!原因是函数 getStringLength 的入参必须是 string 类型!

如此可见,在ts中,所有的一切都需要明确的数据类型,无论是函数入参、变量、常量、还是函数返回值等。定义类型的语法是冒号后面跟上数据类型

基础数据类型

ts提供了以下基础数据类型,定义变量类型的语法是:冒号后面写上类型,如:let userName: string;

类型语法关键字
字符串string
数值number
布尔值boolean
空值void
任意值any
undefinedundefined
nullnull

注意所有数据类型的关键字都是小写开头的!

任意值 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类型的数据,都要具有以下要求:

  1. 必须有name属性且必须是字符串类型;
  2. 必须有age属性且必须是数值类型;
  3. 必须有isAdmin属性且必须是布尔值类型;
  4. 除了以上三个属性,不能再出现其他属性! 有了以上约束,我们就可以非常方便的定义和使用这样“严谨”的数据了。

接口的使用

通过下面的代码,我们定义了一个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: falsesex: 1
} // 程序不会报错,因为User含有任意属性,因此赋值sex属性没问题。

任意属性的语法[propName: string]: any注意:任意属性的数据类型必须是 any!

其他

(数组、函数、枚举、泛型,将在后续的文章中持续更新,敬请期待!)

掘金的第一篇文章,请大家多多支持!