TypeScript 入门:高级类型
类型断言
const nums = [2,66,88]
// TS 的类型推断无法确定res 的结果
const res = nums.find(i => i >0)
// 而此时,res 就不能当作纯数字使用
// const t = res * res // res 报错
// 我们根据代码逻辑,知道res 一定是number,就可以直接断言
const n1 = res as number
// 使用res断言后的结果 n1
const n2 = n1 * n1
console.log(n2)
断言的语法规则就是变量xx as 类型xx或者<类型xx> 变量xx,意思就是告诉 TS ,我的代码一定是某种类型的,放心使用。<类型xx> 变量 xx 的方式在 React 中会与 JSX 语法规则冲突,因此一般更建议使用 as 语法规则。
接口 Interfaces
基本使用
functionprint(post:Objpost){
// 传入的对象中,必须有 title 和 content
console.log(post.title)
console.log(post.content)
}
// 但是,并不能保证调用者一定传入符合条件的对象
print({name:'xiling'})
使用接口进行内容约束:
// 定义接口
interface Objpost{
// 属性名:类型 //使用逗号和分号或者不写都可以
title:string
content:number
}
// 形参使用接口,标注传入的内容,符合接口的定义
functionprint(post:Objpost){
// 传入的对象中,必须有 title 和 content
console.log(post.title)
console.log(post.content)
}
print({name:'xiling'}) // 报错
print({title:'lisi',content:66})
可选与只读属性
// 定义接口
interface Objpost {
// 属性名:类型 //使用逗号和分号或者不写都可以
title: string
content: number
subtitle?: string // 可选成员属性
readonly summary: string // 只读属性,一旦赋值则不可修改
}
// 形参使用接口,标注传入的内容,符合接口的定义
functionprint(post: Objpost) {
// 传入的对象中,必须有 title 和 content
console.log(post.title)
console.log(post.content)
}
let obj:Objpost = { title:'lisi',content:66,summary:'xiling' }
obj.summary='liuneng'// 修改只读属性,报错
print(obj)
动态成员
interfaceCache {
// [定义动态成员:类型]:值类型
[props:string]:string
}
const obj: Cache = {}
// 动态添加对象成员
obj.title='javaScript'
obj.content='html'
console.log(obj)
类的使用
基本使用
class Person{
// 在类中初始化属性定义
name:string='lisi'// 定义初始值
age:number
constructor(name:string,age:number){
this.name=name
this.age=age
this.sex='男'// 没有初始化的成员会报错
}
SayHi(msg:string):void{
console.log(`Hello ${this.name},${msg}`)
}
}
访问修饰符
class Person{
// 类中的修饰符有三种 public private protected
// public 公有属性,任何地方都可以访问
// protected 受保护属性,只有自己和继承的子类中能访问
// private 私有属性,只在类内部访问
public name:string='lisi'
private age:number
protected gender:boolean
constructor(name:string,age:number){
this.name=name
this.age=age
this.gender=true
}
SayHi(msg:string):void{
console.log(`Hello ${this.name},${msg}`)
console.log(this.gender)
}
}
class Student extends Person{
sya(){
this.gender
}
}
let stu =newStudent('lisi',66)
stu.name
stu.age// 私有属性,外部访问报错
stu.gender// 受保护属性,外部访问报错
类的只读属性
class Person{
// 设置成员为只读属性,一旦赋值,不可修改
public readonly name:string='lisi'
constructor(name:string,age:number){
this.name=name
}
SayHi(msg:string):void{
this.name='liuneng'// 修改报错
}
}
类与接口
// 人和动物都有运动和吃东西的行为
// 都有这样的行为,但确实不一样的;
// 这样的情况下,我们就可以使用接口描述不同的行为约束
class Person {
eat(food: string) {
console.log(`使用筷子:${food}`)
}
run(distance: number) {
console.log(`双脚:${distance}`)
}
}
class Animal {
eat(food: string) {
console.log(`撕咬的吃:${food}`))
}
run(distance: number) {
console.log(`四蹄${distance}`)
}
}
人和动物都有运动和吃东西的行为,都有这样的行为,但确实不一样的。这样的情况下,我们就可以使用接口描述不同的行为约束。
// 接口只做行为的约束,不做具体方法的实现
interface eatAndrun {
eat(food: string):void
run(distance: number):void
}
// 使用 implements 关键字进行约束
class Person implements eatAndrun {
eat(food: string) {
console.log(`使用筷子:${food}`)
}
// 如果缺少约束的成员,则会报错
// run(distance: number) {
// console.log(`双脚:${distance}`)
// }
}
但是在实际的开发中,接口约束会更加的细致,就类似摩托车也能跑,但并不是人或动物,细致的约束会更加灵活。
// 对接口进行细致化的拆分
interface eat {
eat(food: string):void
}
interface run {
run(distance: number):void
}
// 使用多个接口,需要逗号,隔开
class Person implements eat,run {
eat(food: string) {
console.log(`使用筷子:${food}`)
}
// 如果缺少约束的成员,则会报错
run(distance: number) {
console.log(`双脚:${distance}`)
}
}
抽象类与抽象方法
抽象类
与接口类似,但是接口只能进行规范约束不能具体实现,而抽象类可以具体实现。抽象类的定义很简单,只需要在类声明的前面添加 abstract 就可以了。
abstract class Animal {
eat(food:string):void{
console.log(`撕咬的吃:${food}`)
}
}
类一旦被抽象,就不能被实例化,只能使用子类继承使用。
// newAnimal() // 报错--无法创建抽象类的实例
class Dog extends Animal{
}
抽象方法
abstract class Animal {
eat(food:string):void{
console.log(`撕咬的吃:${food}`)
}
// 定义抽象方法run , 需要在继承的子类中实现(必须实现)
abstract run(distance:number):void
}
class Dog extends Animal{
// 抽象方法必须在子类中实现
run(distance: number):void {
console.log(distance)
}
}
泛型
在定义函数、接口或者类的时候,没有指定具体的类型,等到使用时才会具体指定的一种特征。
// 函数定义时,参数及返回值并不能确定类型
// 或者,函数需要在运行时固定类型
// 函数名后面使用 <占位符> ,在不确定类型的地方,使用 :占位符
function createArray<T>(length: number, value: T): T[] {
const arr =Array<T>(length).fill(value)
return arr
}
// 函数调用时,使用函数名<泛型的具体类型> (实参1,实参2)
createArray<string>(3,'xl')
类型声明
我们在进行项目开发时,肯定会用到一些第三方模块,但是这些第三方模块不一定是 TS 的,那就无法使用强类型的友好开发体验。
以 lodash 为例,使用 npm install lodash 安装后,通过 import 语法引入。
// 无法找到模块“lodash”的声明文件。
import {camelCase} from'lodash'
// camelCase 将字符串转为驼峰格式
const res =camelCase('hello xiling') // 没有类型提示
console.log(res)
引入后就出现了错误提示:'无法找到模块“lodash”的声明文件。'
同时提示中也告诉我们安装 npm install @types/lodash -D 模块来解决这个问题,已开发依赖的方式安装即可,@types/lodash 其实就是声明文件,成功安装之后,操作就消失了。
而有的模块直接拥有声明文件,这时就不需要额外安装扩展库了。
但是,有一些相对陈旧的库,在使用时自身没有声明文件,也没有对应的文件库这就需要我们自己在代码中进行处理了。
我们依然以 lodash 为例,npm uninstall @types/lodash 卸载类型声明文件,模拟这种情况,无法找到模块“lodash”的声明文件。
camelCase 也没有类型提示:因为确实没有类型声明文件,所以,我们尝试解决 camelCase 的类型提示,其实只需要使用 declare 关键字进行标注就可以了:
// 无法找到模块“lodash”的声明文件。
import {camelCase} from'lodash'
declare functioncamelCase(input:string):string
// camelCase 将字符串转为驼峰格式
const res =camelCase('hello xiling') // 没有类型提示
console.log(res)
Over。
推荐阅读:
更新不易,点个“在看”和“赞”吧(●'◡'●)!