基础语法
类型注解
这是TypeScript
最基础的语法,为变量指明类型,称为类型注解,语法为:变量:类型
let isDone: boolean = false
let isDone: boolean = false
基础类型
原始数据类型
JavaScript
分为原始数据类型和对象类型,原始数据类型有:number
、string
、boolean
、null
、undefined
在TypeScript
中,相应类型注解为:
let tsNum: number = 123
let tsStr: string = 'abc'
let tsFlag: boolean = true
let tsNull: null = null
let tsUndefined: undefined = undefined
let tsNum: number = 123
let tsStr: string = 'abc'
let tsFlag: boolean = true
let tsNull: null = null
let tsUndefined: undefined = undefined
null与undefined
在TypeScript
中,null
和undefined
是所有类型的子类型,也就是说可以把undefined
或null
赋值给原始数据类型的变量
let tsNumber1: number = undefined
let tsNumber2: number = null
let tsNumber1: number = undefined
let tsNumber2: number = null
void
JavaScript
中,是没有空值(void
)的概念的,但在TypeScript
中,可以使用void
来表示一个没有返回值的函数
function sayHello (): void {
console.log('Hello, world')
}
function sayHello (): void {
console.log('Hello, world')
}
也可以定义一个void
类型的变量,不过这样的变量并没有什么意义,因为只能给这种变量赋值为null
或undefined
这也是void
跟null
和undefined
的区别,对于void
而言,它只能被赋值为null
或者undefined
let voidValue1: void = null
let voidValue2: void = undefined
// 报错
let voidValue1: void = 123
let voidValue2: void = 'abc'
let voidValue1: void = null
let voidValue2: void = undefined
// 报错
let voidValue1: void = 123
let voidValue2: void = 'abc'
any
任意值any
用来表示可以接受任何类型的值
以下代码会报错,因为变量被定义为number
,那么它只能接受number
类型的值,改变其类型,会编译报错
let tsNumber: number = 123
tsNumber = '123'
let tsNumber: number = 123
tsNumber = '123'
但是如果一个变量被定义为any
,那么代表它可以接受任何类型的值:
// 类型正确
let tsAny: any = 123
tsAny = '123'
// 类型正确
let tsAny: any = 123
tsAny = '123'
如果定义了一个变量,没有指定其类型,也没有初始值,那么它默认为any
类型
// 类型正确,不会报错,因为默认为any类型
let tsValue
tsValue = 123
tsValue = '123'
// 类型正确,不会报错,因为默认为any类型
let tsValue
tsValue = 123
tsValue = '123'
接口
接口interface
用于为对象提供类型注解, 接口interface
中的任何代码都不会被最后编译到JavaScript
中
interface Person {
name: string,
age: number
}
let person: Person = {
name: 'why',
age: 23
}
interface Person {
name: string,
age: number
}
let person: Person = {
name: 'why',
age: 23
}
在上方代码中,person
对象的类型是接口Person
,那么person
对象的所有属性只能是Person
规定的属性,且属性值的类型也必须和Person
中规定的一致,多一个属性或者少一个属性都会报错
interface Person {
name: string,
age: number
}
// 报错
let person1: Person = {
name: 'why'
}
// 报错
let person2: Person = {
name: 'why',
age: 23,
sex: 'man'
}
interface Person {
name: string,
age: number
}
// 报错
let person1: Person = {
name: 'why'
}
// 报错
let person2: Person = {
name: 'why',
age: 23,
sex: 'man'
}
接口中的任意属性
以上一个例子为基础,假设接口只对name
和age
做规定,其它任何属性名与属性类型都是可以的,那么可以如下方式进行定义:
interface Person {
name: string,
age: number,
// 任意属性名与属性类型
[propName: string]: any
}
// 类型正确
let person: Person = {
name: 'why',
age: 23,
sex: 'man'
}
interface Person {
name: string,
age: number,
// 任意属性名与属性类型
[propName: string]: any
}
// 类型正确
let person: Person = {
name: 'why',
age: 23,
sex: 'man'
}
接口中的可选属性
?:可选属性,代表该属性可存在可不存在,但存在的话类型必须无误
interface Person {
name: string,
age?: number // age属性是可选的
}
// 类型正确
let person1: Person = {
name: 'why'
}
// 报错
let person2: Person = {
name: 'why',
age: "123"
}
interface Person {
name: string,
age?: number // age属性是可选的
}
// 类型正确
let person1: Person = {
name: 'why'
}
// 报错
let person2: Person = {
name: 'why',
age: "123"
}
接口中的只读属性
readonly表示属性只读,不可修改,在接口中标记了属性为只读的, 那么其不能被赋值。
interface Person {
name: string,
readonly age: number
}
let person: Person = {
name: 'why',
age: 23
}
// 报错
person.age = 32
interface Person {
name: string,
readonly age: number
}
let person: Person = {
name: 'why',
age: 23
}
// 报错
person.age = 32
函数类型
为参数和返回值设置类型,不定义相应类型的话,默认为any
/**
* @param x 数字1
* @param y 数字2
* @returns 数字1和数字2之和
*/
function add (x: number, y: number): number {
return x + y
}
console.log(add(1, 2)) // 输出3
console.log(add(1, '2')) // 报错
/**
* @param x 数字1
* @param y 数字2
* @returns 数字1和数字2之和
*/
function add (x: number, y: number): number {
return x + y
}
console.log(add(1, 2)) // 输出3
console.log(add(1, '2')) // 报错
也可以使用interface为函数设置类型
interface AddInterface {
(x: number, y: number): number
}
/**
* @param x 数字1
* @param y 数字2
* @returns 数字1和数字2之和
*/
const add: AddInterface = (x, y) => {
return x + y
}
console.log(add(1, 2)) // 输出3
console.log(add(1, '2')) // 报错
interface AddInterface {
(x: number, y: number): number
}
/**
* @param x 数字1
* @param y 数字2
* @returns 数字1和数字2之和
*/
const add: AddInterface = (x, y) => {
return x + y
}
console.log(add(1, 2)) // 输出3
console.log(add(1, '2')) // 报错
可选参数
函数接受可选的参数,使用?:
function getArea (a: number, b?: number): number {
return b ? a * b : a * a
}
console.log(getArea(4)) // 16
console.log(getArea(4, 5)) // 20
function getArea (a: number, b?: number): number {
return b ? a * b : a * a
}
console.log(getArea(4)) // 16
console.log(getArea(4, 5)) // 20
WARNING
可选参数必须放在最后一个位置,否则会报错
// 报错
function getArea (b?: number, a: number): number {
return b ? a * b : a * a
}
// 报错
function getArea (b?: number, a: number): number {
return b ? a * b : a * a
}
参数默认值
在JavaScript
中,函数允许给参数设置默认值,因此另外一种处理可选参数的方式是,为参数提供一个默认值,此时TypeScript
将会把该参数识别为可选参数:
function getArea (a: number, b: number = 1): number {
return a * b
}
console.log(getArea(4)) // 4
console.log(getArea(4, 5)) // 20
function getArea (a: number, b: number = 1): number {
return a * b
}
console.log(getArea(4)) // 4
console.log(getArea(4, 5)) // 20
TIP
给一个参数设置了默认值后,就不再受TypeScript
可选参数必须在最后一个位置的限制了
function getArea (b: number = 1, a: number): number {
return a * b
}
// 传递一个undefined占位,undefined不影响参数1的默认值,仅作占位用
console.log(getArea(undefined,4)) // 4
console.log(getArea(4, 5)) // 20
function getArea (b: number = 1, a: number): number {
return a * b
}
// 传递一个undefined占位,undefined不影响参数1的默认值,仅作占位用
console.log(getArea(undefined,4)) // 4
console.log(getArea(4, 5)) // 20
剩余参数
在ES6
中,可以使用...
符号进行传递剩余参数,在TypeScript
中,依然可以这么做
// rest是一个数组,可以使用数组的类型来定义它
function getTotal (a: number, ...rest: number[]) {
console.log(a) // 1
console.log(rest) // [2, 3, 4]
}
getTotal(1, 2, 3, 4)
// rest是一个数组,可以使用数组的类型来定义它
function getTotal (a: number, ...rest: number[]) {
console.log(a) // 1
console.log(rest) // [2, 3, 4]
}
getTotal(1, 2, 3, 4)
函数重载
因为在JavaScript
中,并没有限制函数参数的个数或者类型,因此JavaScript
没有函数重载
的概念
在TypeScript
中对于函数重载的理解是:只要函数参数的类型或者函数参数的数量不同时,就可以认为这是两个函数(重载
)
下方示例,前三个都是重载
,最后一个才是实现
,因为实现
只包含了前两个重载
,所以第三个重载
会报错
function add(a: number, b: number): number
function add(a: string, b: string): string
// 报错,此重载签名与其实现签名不兼容
function add(a: boolean, b: boolean): boolean
function add(a: number | string, b: number | string): number | string {
if (typeof a === "number" && typeof b === "number") {
return a + b
} else {
return a + "" + b
}
}
console.log(add(1, 2)) // 3
console.log(add("1", "2")) // 12
function add(a: number, b: number): number
function add(a: string, b: string): string
// 报错,此重载签名与其实现签名不兼容
function add(a: boolean, b: boolean): boolean
function add(a: number | string, b: number | string): number | string {
if (typeof a === "number" && typeof b === "number") {
return a + b
} else {
return a + "" + b
}
}
console.log(add(1, 2)) // 3
console.log(add("1", "2")) // 12
在有函数重载时,会优先从第一个进行逐一匹配,因此如果重载函数有包含关系,应该将最精准的函数定义写在最前面
数组类型
两种方法定义数组类型
方法一 数组泛型,Array<type>
let list: Array<number> = [1, 2, 3]
let list: Array<number> = [1, 2, 3]
方法二 type[ ]
let list: number[] = [1, 2, 3]
let list: number[] = [1, 2, 3]
联合类型数组
let tsArray: (number | string) [] = ["1", '2', 3]
let tsArray: (number | string) [] = ["1", '2', 3]
对象类型数组
let objArray: { name: string; age: number }[] = [{ name: "AAA", age: 23 }]
let objArray1: Array<{name: string; age: number}> = [{name: "AAA", age: 23}]
interface obj {
name: string
age: number
}
let objArray2: obj[] = [{ name: "AAA", age: 23 }]
let objArray3: Array<obj> = [{ name: "AAA", age: 23 }]
let objArray: { name: string; age: number }[] = [{ name: "AAA", age: 23 }]
let objArray1: Array<{name: string; age: number}> = [{name: "AAA", age: 23}]
interface obj {
name: string
age: number
}
let objArray2: obj[] = [{ name: "AAA", age: 23 }]
let objArray3: Array<obj> = [{ name: "AAA", age: 23 }]
元组
一个数组的长度是确定的,且每个位置的值的类型也是确定的,那么就可以把这样的数组称为元组
let nameNumber: [string, number] = ['1',2]
let nameNumber: [string, number] = ['1',2]
当访问元组中已知位置的索引时,将得到其对应正确的值;当访问元组中未知位置的索引时,会报错
let tuple: [string, number] = ['AAA', 123]
console.log(tuple[1]) // 123
console.log(tuple[2]) // 报错
let tuple: [string, number] = ['AAA', 123]
console.log(tuple[1]) // 123
console.log(tuple[2]) // 报错
类型推断
在以上的所有实例中,为每一个变量提供了相应的类型,也就是类型注解。而有些时候,没有为其提供一个确定的类型,但提供了一个确定的值,那么TypeScript
会根据给定的值的类型自动推断出这个变量的类型,称为类型推断
let tsNum = 123 // 类型推断为number
let tsStr = "abc" // 类型推断为string
let tsFlag = true // 类型推断为boolean
let tsNull = null // 类型推断为any
let tsUndefined = undefined // 类型推断为any
const helloWorld = {
hello: 123,
world: "abc"
}
// 类型推断helloWorld的类型为
// const helloWorld = {
// hello: number,
// world: string
// }
let tsNum = 123 // 类型推断为number
let tsStr = "abc" // 类型推断为string
let tsFlag = true // 类型推断为boolean
let tsNull = null // 类型推断为any
let tsUndefined = undefined // 类型推断为any
const helloWorld = {
hello: 123,
world: "abc"
}
// 类型推断helloWorld的类型为
// const helloWorld = {
// hello: number,
// world: string
// }
由上方例子可见,null和undefined不能推断
另外,函数的参数无法推断,必须指定,但返回值可以推断,但建议写上,更加清晰
function add (num1: number, num2: number): number {
return num1 + num2
}
// 可以省略函数的返回值类型,因为typescript会基于num1和num1全部为number类型,从而推断出函数返回值为number类型
function add (num1: number, num2: number) {
return num1 + num2
}
function add (num1: number, num2: number): number {
return num1 + num2
}
// 可以省略函数的返回值类型,因为typescript会基于num1和num1全部为number类型,从而推断出函数返回值为number类型
function add (num1: number, num2: number) {
return num1 + num2
}
联合类型
表示取值可以为多种类型中的一种,多种类型使用|
分隔开
let value: string | number
value = 123
value = '123'
let value: string | number
value = 123
value = '123'
当使用联合类型的时候,因为TypeScript
不确定到底是哪一个类型,所以TypeScript
只允许此联合类型中所有类型公用的属性和方法
// length是string类型独有的属性,不是公用的属性,所以会报错
function getLength (value: string | number): number {
return value.length
}
// toString是string类型和number类型公用的方法,不会报错
function valueToStr (value: string | number): string {
return value.toString()
}
// length是string类型独有的属性,不是公用的属性,所以会报错
function getLength (value: string | number): number {
return value.length
}
// toString是string类型和number类型公用的方法,不会报错
function valueToStr (value: string | number): string {
return value.toString()
}
当联合类型被赋值后,
TypeScript
会根据类型推断来确定变量的类型,一旦确定后,则此变量只能使用这种类型的属性和方法
let tsValue: string | number
tsValue = '123'
console.log(tsValue.length) // 类型正确
tsValue = 123
console.log(tsValue.length) // 报错
let tsValue: string | number
tsValue = '123'
console.log(tsValue.length) // 类型正确
tsValue = 123
console.log(tsValue.length) // 报错
交叉类型
一般适用于对象或者函数的"合并",使用&
符号进行连接
type result = T & U
type result = T & U
T & U
表示一个新的类型,其中这个类型包含T
和U
中所有的键,这和JavaScript
中的Object.assign()
函数的作用非常类似
function merge<T, U>(to: T, from: U): T & U {
for (let key in from) {
;(to as T & U)[key] = from[key] as any
}
return to as T & U
}
const obj1 = { str: "abc" }
const obj2 = { num: 123 }
const result = merge(obj1, obj2)
function merge<T, U>(to: T, from: U): T & U {
for (let key in from) {
;(to as T & U)[key] = from[key] as any
}
return to as T & U
}
const obj1 = { str: "abc" }
const obj2 = { num: 123 }
const result = merge(obj1, obj2)
类型断言
有些情况下typescript没办法自动推断出正确的类型, 就需要手动告诉typescript这个类型
断言有2种语法, 一种是通过<>
, 一种通过as
// ts推断类型为number|number[],实际上是number[]类型
let obj = 0.5 < 0.2 ? 0 : [0]
// 报错,类型“number | number[]”上不存在属性“push”。类型“number”上不存在属性“push”
obj.push(1)
// 告诉ts, obj为数字数组类型
(obj as number[]).push(2)
// 告诉ts, obj为数字数组类型
(<number[]>obj).push(3)
// ts推断类型为number|number[],实际上是number[]类型
let obj = 0.5 < 0.2 ? 0 : [0]
// 报错,类型“number | number[]”上不存在属性“push”。类型“number”上不存在属性“push”
obj.push(1)
// 告诉ts, obj为数字数组类型
(obj as number[]).push(2)
// 告诉ts, obj为数字数组类型
(<number[]>obj).push(3)
类型别名
type
关键字来给一个类型起一个新的名字,类型别名常用于联合类型
type Add = (x: number, y: number) => number
const add: Add = (a, b) => a + b
add(1,2) // 类型正确
add('1',2) // 报错,类型“string”的参数不能赋给类型“number”的参数。ts(2345)
type Add = (x: number, y: number) => number
const add: Add = (a, b) => a + b
add(1,2) // 类型正确
add('1',2) // 报错,类型“string”的参数不能赋给类型“number”的参数。ts(2345)
与其他类型组合
interface A {
a: number
}
type B = A | {b: string}
type C = A & {b: string}
const aa: B = {
a: 1
}
const bb: B = {
b: "1"
}
const cc: C = {
a: 1,
b:'1'
}
// 与泛型组合
type D<T> = A | T[]
interface A {
a: number
}
type B = A | {b: string}
type C = A & {b: string}
const aa: B = {
a: 1
}
const bb: B = {
b: "1"
}
const cc: C = {
a: 1,
b:'1'
}
// 与泛型组合
type D<T> = A | T[]
类型别名和接口的区别
type和interface都可以定义对象类型
区别在于:
- type可以定义interface不能定义的类型,如联合类型,字面量类型
- type还可以与其他类型组合
- interface可以继承、声明合并
字面量类型
字面量类型用来表示一个变量只能取某几个字符串值中的一个
type eventName = 'click' | 'scroll' | 'mousemove'
function handleEvent (event: eventName) {
console.log(event)
}
handleEvent('click')
handleEvent('scroll')
handleEvent('dbclick') // 报错
type eventName = 'click' | 'scroll' | 'mousemove'
function handleEvent (event: eventName) {
console.log(event)
}
handleEvent('click')
handleEvent('scroll')
handleEvent('dbclick') // 报错
枚举
枚举Enum
类型用来表示取值限定在指定的范围,例如一周只能有七天,颜色只能有红、绿、蓝等
enum colors {
red,
green,
blue
}
console.log(colors.red) // 0
console.log(colors.green) // 1
console.log(colors.blue) // 2
enum colors {
red,
green,
blue
}
console.log(colors.red) // 0
console.log(colors.green) // 1
console.log(colors.blue) // 2
定义一个colors
的枚举类型,其取值只能是red
、green
、blue
。从打印的内容中发现,其输出值从0开始,依次累加1。这是枚举类型的默认行为,可以手动设置一个起始值
enum colors {
red = 10,
green,
blue
}
console.log(colors.red) // 10
console.log(colors.green) // 11
console.log(colors.blue) // 12
enum colors {
red = 10,
green,
blue
}
console.log(colors.red) // 10
console.log(colors.green) // 11
console.log(colors.blue) // 12
在枚举类型中,不仅可以正向的获取值,还可以通过值反向获取枚举
enum colors {
red = 10,
green,
blue
}
console.log(colors[10]) // red
console.log(colors[11]) // green
console.log(colors[12]) // blue
enum colors {
red = 10,
green,
blue
}
console.log(colors[10]) // red
console.log(colors[11]) // green
console.log(colors[12]) // blue
泛型
根据传入的参数自动判断类型
function a<T>(arr:T[]):T[] {
return arr
}
let b = a([1,2,3])
b = [1] // 类型正确
b = ['1'] // 报错,不能将类型“string”分配给类型“number”
function a<T>(arr:T[]):T[] {
return arr
}
let b = a([1,2,3])
b = [1] // 类型正确
b = ['1'] // 报错,不能将类型“string”分配给类型“number”