这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战
原始数据类型
原始数据类型包括:
- Boolean
- String
- Number
- Null
- undefined
类型声明是TS非常重要的一个特点,通过类型声明可以指定TS中变量、参数、形参的类型。
-
Boolean 类型
let boolean: boolean = true boolean = false boolean = null // bollean = 123 报错不可以将数字 123 赋值给 boolean类型的变量 -
Number 类型
//ES6 Number 类型 新增支持2进制和8进制 let num: number = 123 num = 0b1111 -
String 类型
let str1: string = 'hello TS' let sre2: string = `模板字符串也支持使用 ${str1}`] -
Null 和 Undefined
let n: null = null let u: undefined = undefined n = undefined u = null // undefined 和 null 是所有类型的子类型 所以可以赋值给number类型的变量 let num: number = 123 num = undefined num = null
any 类型
any 表示队变量没有任何显示,编译器失去了对 TS 的检测功能与 JS 无异(不建议使用)。
let notSure: any = 4
// any类型可以随意赋值
notSure = `任意模板字符串`
notSure = true
notSure = null
// 当 notSure 为any 类型时,在any类型上访问任何属性和调用方法都是允许的, 很有可能出现错误
notSure.name // 现在调用name属性是允许的,但很明显我们定义的notSure没有name这个属性,下面的调用sayName方法也是如此
notSure.sayName()
array 类型
// 数组类型,可以指定数组的类型和使用数组的方法和属性
let arrOfNumbers: number[] = [1, 2, 3]
console.log(arrOfNumbers.length);
arrOfNumbers.push(4)
tuple 元组类型
// 元组类型 元组就是固定长度,类型的数组
// 类型和长度必须一致
let u: [string, number] = ['12', 12]
// let U: [string, number] = ['12', 12, true] 报错信息为:不能将类型“[string, number, boolean]”分配给类型“[string, number]”。源具有 3 个元素,但目标仅允许 2 个。
// 也可以使用数组的方法,如下所示push一个值给元组u
u.push(33)
Interface 接口
- 对对象的形状(shape)进行描述
- Duck Typing(鸭子类型)
interface Person {
// readonly id 表示只读属性的id不可以修改
readonly id: number;
name: string;
age: number;
// weight? 表示可选属性,可以选用也可以不选用
weight?: number
}
let host: Person = {
id: 1,
name: 'host',
age: 20,
weight: 70
}
//host.id = 2 报错 提示信息为:无法分配到 "id" ,因为它是只读属性
function 函数类型
// 方式一:函数声明的写法 z 为可选参数 ,
function add1 (x: number, y: number, z?: number): number {
if (typeof z === 'number') {
return x + y + z
} else {
return x + y
}
}
// 需要注意的是:可选参数必须置于所有必选参数之后,否则会报错
add1(1, 2, 3)
// 方式二:函数表达式
const add2 = (x: number, y: number, z?: number): number => {
if (typeof z === 'number') {
return x + y + z
} else {
return x + y
}
}
// 使用interface接口 描述函数类型
interface ISum {
(x: number, y: number, z?: number): number
}
let add3: ISum = add1
值的注意的是:可选参数必须置于所有必选参数之后,否则会报错,如下图展示的错误案例所示:·
类型推论
当定义变量时没有指定类型,编译器会自动推论第一次赋的值为默认类型
let s = 'str'
// s = 12 本句将会报错,提示为:不能将类型“number”分配给类型“string”
联合类型
使用 | 分隔可选类型
let StringOrNumber: string | number
StringOrNumber = 123
StringOrNumber = '111'
类型断言
使用 as 关键字进行类型断言
function getLength (rod: number | string): number {
const str = rod as string
//这里我们可以用 as 关键字,告诉typescript 编译器,你没法判断我的代码,但是我本人很清楚,这里我就把它看作是一个 string,你可以给他用 string 的方法。
if (str.length) {
return str.length
} else {
const num = rod as number
return num.toString().length
}
}
类型守卫
// 4.类型守卫 type guard typescript 在不同的条件分支里面,智能的缩小了范围
function getLength2 (rod: number | string): number {
if (typeof rod === 'string') {
return rod.length
} else {
// else 里面的rod 会自动默认为number类型
return rod.toString().length
}
}
Class 类
面向对象编程的三大特点:
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性。
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。
话不多少,看代码:
class Animal {
readonly name: string
constructor(name: string) {
this.name = name
console.log(this.run())
}
// private run ():私有的 protected run () 受保护的
run () {
return `${this.name} is running`
}
}
const animal = new Animal('elephant')
// console.log(animal.name)
animal.run() //elephant is running
// 继承
class Dog extends Animal {
age: number
constructor(name, age) {
super(name)
console.log(this.name)
this.age = age
}
bark () {
console.log(`这只在叫的狗狗叫${this.name},它今年${this.age}岁了`)
}
}
const dog = new Dog('旺财', 5)
dog.run() // 旺财 is running
dog.bark() // 这只在叫的狗狗叫旺财,它今年5岁了
// 多态
class Cat extends Animal {
static catAge = 2
constructor(name) {
super(name)
console.log(this.name) // 布丁
}
run () {
return 'Meow,' + super.run()
}
}
const cat = new Cat('布丁')
console.log(cat.run()) // Meow,布丁 is running
console.log(Cat.catAge) // 2
class中还提供了readonly关键字,readonly为只读属性,在调用的时候不能修改。如下所示:
类成员修饰符
-
public修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是public的。
-
private修饰的属性或方法是私有的,不能在声明它的类的外部访问。
上述示例代码中,在父类Animal的 run 方法身上加上private修饰符之后就会产生如下图的报错信息:
-
protected 修饰的属性或方法是受保护的,它和private类似,区别在于它在子类中也是可以访问的。
上述示例代码中,在父类Animal的 run 方法身上加上protected修饰符之后就会产生如下图的报错信息:
接口和类
类可以使用 implements来实现接口。
// interface可以用来抽象验证类的方法和方法
interface Person {
Speak (trigger: boolean): void;
}
interface Teenagers {
Young (sge: number): void
}
// 接口之间的继承
interface PersonAndTeenagers extends Teenagers {
Speak (trigger: boolean): void;
}
// implements 实现接口
class Boy implements Person {
Speak (mouth: boolean) { }
}
// class Girl implements Person, Teenagers 和 class Girl implements PersonAndTeenagers 作用相同
class Girl implements PersonAndTeenagers {
Speak (mouth: boolean) { }
Young (sge: number) { }
}
enum枚举
枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射:
enum Color {
red = 'red',
blue = 'blue',
yellow = 'yellow',
green = 'green'
}
// 常量枚举
const enum Color {
red = 'red',
blue = 'blue',
yellow = 'yellow',
green = 'green'
}
console.log(Color.red) // 0
// 反向映射
console.log(Color[0]) // red
const value = 'red'
if (value === Color.red) {
console.log('Go Go Go ')
- 常量枚举经过编译后形成的js文件如下:
- 非常量枚举经过编译器编译之后的js文件如下:
Generics 泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
- 约束泛型
- 类与泛型
- 接口与泛
示例代码如下:
function echo (arg) {
return arg
}
let result1 = echo(123) // 参数传递123后result1 的类型为any
// 泛型
function echo2<T> (arg: T): T {
return arg
}
let result2 = echo2(123) // 加上泛型之后 参数传递 123后result2的类型为number
function swap<T, U> (tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
console.log(swap(['hero', 123]))//[ 123, 'hero' ]
// 约束泛型
interface IWithLength {
length: number
}
function echoWithLength<T extends IWithLength> (arg: T): T {
console.log(arg.length)
return arg
}
const str = echoWithLength('123')
const obj = echoWithLength({ length: 3, name: 'Tom' })
const arr = echoWithLength([1, 2, 3, 4])
// 类与泛型
class Queue<T> {
private data = []
push (item: T) {
return this.data.push(item)
}
pop (): T {
return this.data.shift()
}
}
const queue = new Queue<number>()
queue.push(1)
console.log(queue.pop().toFixed())// 1
// 接口与泛型
interface KeyPair<T, U> {
key: T
value: U
}
let kp1: KeyPair<string, number> = { key: 'str', value: 123 }
let kp2: KeyPair<number, string> = { key: 123, value: 'str' }
let arr2: Array<string> = ['1', '2'] // 使用 Array<string> 等价于 interface Array<T>
类型别名 type-alias
类型别名,就是给类型起一个别名,让它可以更方便的被重用。
let sum: (x: number, y: string) => number
const result1 = sum(1, '2')
// 将(x: number, y: string) => number类型取一个别名 为 PlusType
type PlusType = (x: number, y: string) => number
let sum2: PlusType
const result2 = sum2(2, '2')
type StrOrNum = string | number
let result3: StrOrNum = 123
result3 = '123'
字面量
let Name: 'name' = 'name'
// Name = '123' //报错信息:不能将类型“"123"”分配给类型“"name"”
let age: 19 = 19
type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let up: Directions = 'Up'
交叉类型
// 交叉类型 使用 ‘&’ 符号进行类型的扩展
interface IName {
name: string
}
type IPerson = IName & { age: number }
let person: IPerson = { name: 'Tom', age: 19 }
内置类型
- 全局对象
// global objects 全局对象
const a: Array<string> = ['123', '456']
const time = new Date()
time.getTime()
const reg = /abc/ // 此时reg为RegExp类型
reg.test('abc')
-
build-in object 内置对象
Math.pow(2, 2) //返回 2 的 2次幂。 console.log(Math.pow(2, 2)) // 4 -
DOM and BOM
// document 对象,返回的是一个 HTMLElement let body: HTMLElement = document.body // document 上面的query 方法,返回的是一个 nodeList 类型 let allLis = document.querySelectorAll('li') //当然添加事件也是很重要的一部分,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性。 document.addEventListener('click', (e) => { e.preventDefault() }) -
utility types实用类型
interface IPerson2 { name: string age: number } let viking: IPerson2 = { name: 'viking', age: 20 } // partial,它可以把传入的类型都变成可选 type IPartial = Partial<IPerson> let viking2: IPartial = {} // Partial 将IPerson 中的类型变成了可选类型 所以 viking2 可以等于一个空对象 // Omit,它返回的类型可以忽略传入类型的某个属性 type IOmit = Omit<IPerson2, 'name'> // 忽略name属性 let viking3: IOmit = { age: 20 }
如果加上name属性将会报错:不能将类型“{ age: number; name: string; }”分配给类型“IOmit”。对象文字可以只指定已知属性,并且“name”不在类型“IOmit”中。