介绍
TypeScript的作者是安德斯·海尔斯伯格,C#的首席架构师。
它是开源和跨平台的编程语言。它是JavaScript的一个超集,
而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。
背景
TypeScript 起源于使用JavaScript开发的大型项目。
由于JavaScript语言本身的局限性,难以胜任和维护大型项目开发。
因此微软开发了TypeScript ,使得其能够胜任开发大型项目。
ts优点
1.ts增加了代码的可读性和可维护性
* 类型系统实际上是最好的文档,大部分的函数看看类型的定义就知道如何使用了
* 可在编译阶段就发现大部分错误,总比在运行时出错好
2.ts非常包容
* ts是js的超集,.js文件直接重命名为.ts即可
* 即使不显示的定义类型,也能够自动做出类型推断
* 可以定义从简单到复杂的几乎一切类型
* 即使ts编译报错,也可以生成js文件
* 兼容第三方库,即使第三方库不使用ts写的,也可以编写单独的类型文件供ts读取
ts缺点
* 有一定的学习成本,需要理解接口(Interface)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端可能不太接触的概念
* 集成到构建流程需要一些工作量
基础类型
布尔值
- 布尔值是最基础的数据类型,在ts中,使用
boolean定义布尔值类型
let isDone: boolean = false
数值
- 使用
number定义数值类型
let decLiteral: number = 6
字符串
使用string定义字符串类型
let name: string = 'yxx'
let age: number = 18
let personInfo: string = `你好,我的名字叫${name},今年${age+1}岁`
编译结果:
var name = 'yxx'
var age = 18
var personInfo = "你好,我的名字叫" + name + ",今年" + age + "岁"
空值
js中没有空值(Void)的概念,在ts中可以用void表示没有任何返回值的函数
function fn(): void {
alert('My name is yxx')
}
声明1个void类型的变量没有什么用,你只能将他赋值为null和undefined
null和undefined
let u: undefind = undefind
let n: null = null
与void的区别是,undefind和null是所有类型的子类型。
let num: number = undefind
or
let u: undefind = undefind
let num: number = u
而void类型的变量不能赋值给number类型的变量
let u: void
let num: number = u
// Type 'void' is not assignable to type 'number'
任意值
任意值(Any)用来表示允许赋值为任意类型。
声明一个变量为任意值之后,对他的任何操作,返回内容的类型都是任意值
变量如果在声明的时候未指定其类型,那么他会被识别为任意类型
let something: any
类型推论
以下代码虽然没有指定类型,但会在编译时报错:
let num = 'seven'
num = 7
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
他等价于
let num: string = 'seven'
num = 7
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
ts会在没有明确指定类型的时候推测出一个类型,这就是类型推论
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成any类型而完全不被类型检查
联合类型
联合类型(Union Types)表示取值可以为多重类型的一种
let num: string | number = 'seven'
num = 7
联合类型使用|分隔每个类型
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型:
let num: string | number
num = 'seven'
console.log(num.length) // 5
num = 7
console.log(num.length) // 编译时报错
// error TS2339: Property 'length' does not exist on type 'number'.
上例中,第二行的 num 被推断成了 string,访问它的 length 属性不会报错。
而第四行的 num 被推断成了 number,访问它的 length 属性时就报错了。
接口
在ts中,我们使用接口(Interface)来定义对象的类型。
ts中的接口是一个非常灵活的概念,除了可以用对类的一部分行为进行抽象以外,也常用于对 对象的形状进行描述
interface Person {
name: string
age: number
}
let per: Person = {
name: 'yxx',
age: 18
}
这里面定义了一个接口Person,定义了一个变量per,它的类型是Person,这样就约束了per的形状必须和接口Person一致
多一个或者少一个属性都会报错
可选属性
有时我们希望不要完全匹配一个形状,那么可以用可选属性
interface Person {
name: string
age?: number
}
let per: Person = {
name: 'yxx'
}
可选属性的含义是该属性可以不存在,这时仍然不允许添加未定义属性
任意属性
有时我们希望一个接口允许有任意的属性,可以这样
interface Person {
name: string
age?: number
[propName: string]: any
}
let per: Person = {
name: 'yxx'
sex: '男'
}
使用 [propName: string] 定义了任意属性取 string 类型的值。
只读属性
有时我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用readonly定义只读属性
interface Person {
readonly id: number
name: string
age?: number
[propName: string]: any
}
let per: Person = {
id: 1,
name: 'yxx'
}
per.id = 2
// error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
上例中,使用readonly定义的属性id初始化后,又被赋值了,所以报错
数组
在ts中,数组类型有多种定义方式,比较灵活
类型+方括号 表示法
let arr: number[] = [1,2,3,4]
// 数组的项中不允许出现其他的类型:
let arr1: number[] = [1,2,'3',4]
// Type 'string' is not assignable to type 'number'.
数组泛型
可以用数组泛型Array<elemType>来表示数组
let arr: Array<number> = [1,2,3,4]
用接口表示数组
interface NumberArray {
[index: number]: number
}
let arr: NumberArray = [1,2,3,4]
NumberArray表示: 只要索引的类型是数字时,那么值得类型必须是数字
函数
在js中,有两种常见的定义函数的方式--函数声明和函数表达式
// 函数声明
function sum(x, y) {
return x + y
}
// 函数表达式
let sum = function (x, y) {
return x + y
}
一个函数有输入和输出,要在ts中对其进行约束,需要把输入和输出都考虑到
函数声明的类型定义:
function sum(x: number, y: number): number {
return x + y
}
// 注意:输入多余的(或者少于规定)的参数,会报错
sum(1,2,3)
// error TS2346: Supplied parameters do not match any signature of call target.
函数表达式
let sum = function (x: number, y: number): number {
return x + y;
}
这样写是对等号右边的匿名函数进行了类型定义,而左边的sum是通过类型推论推断出来的,如果我们手动给sum添加类型,应该是这样:
let sum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
}
用接口定义函数的形状
interface Func {
(firstName: string, lastName: string): string
}
let personName: Func
personName = function (firstName: string, lastName: string) {
return `我的名字是${firstName}${lastName}`
}
可选参数
同接口中的可选属性一样
function personName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName
} else {
return firstName
}
}
personName('叶')
参数默认值
function personName(firstName: string = "叶", lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName
} else {
return firstName
}
}
personName('叶')
重载
重载允许一个函数接受不同数量或类型的参数时,做出不同的处理
比如,我们需要实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 'hello' 的时候,输出反转的字符串 'olleh'。
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (type x === 'string') {
return x.split('').reverse().join('')
}
}
这样做有一个缺点,他不能精确的表达,输入为数字,输出也应该为数字,输入为字符串,输出也应为字符串
这时可以用重载定义多个reverse的函数类型
function reverse(x: number): number
function reverse(x: string): string
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (type x === 'string') {
return x.split('').reverse().join('')
}
}
类型断言
类型断言的用途分以下几种:
1. 将一个联合类型断言为其中一个类型
当ts不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型中共有的属性或方法:
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function getName(animal: Cat | Fish) {
return animal.name;
}