TypeScript由浅到深(上篇)
目录
一、什么是TypeScript有什么特点:
二、TypeScript的编译环境:
三、TypeScript数据类型:
01_标识符的类型推导:
02_JS中的类型Array:
03_JS 中的类型Object:
04_函数的类型:
05_匿名函数的类型:
06_TS中的类型:
四、TypeScript语法细讲 :
TS中联合类型的使用:
TS中的类型别名 :
TS中的接口声明使用:
TS中交叉类型的使用:
TS中的类型断言as:
TS中非空类型断言:
TS中的字面量类型的使用:
TS中类型缩小的使用:
五、TypeScript函数类型
函数类型:
调用签名(Call Signatures):
构造签名 :
函数的参数-可选参数:
函数的参数-默认参数:
函数的参数-剩余参数 :
函数的重载:
联合类型与重载:
可推导的this类型:
this相关的内置工具:
六、TypeScript面向对象:
TS中类的基本使用:
只读属性readonly:
getters/setters :
抽象类abstract:
类型检测-鸭子类型:
索引签名:
接口的继承和实现:
抽象类和接口的区别(了解):
严格的字面量赋值检测:
TypeScript枚举类型:
一、什么是TypeScript有什么特点:
可以看一下TypeScript在GitHub和官方上对自己的定义:
GitHub说法:TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
TypeScript官网:TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
我们可以将TypeScript理解成加强版的JavaScript。 JavaScript所拥有的特性,TypeScript全部都是支持的,并且它紧随ECMAScript的标准,所以ES6、ES7、ES8等新语法标准,它都是 支持的; TypeScript在实现新特性的同时,总是保持和ES标准的同步甚至是领先;并且在语言层面上,不仅仅增加了类型约束,而且包括一些语法的扩展,比如枚举类型(Enum)、元组类型(Tuple)等; 并且TypeScript最终会被编译成JavaScript代码,所以你并不需要担心它的兼容性问题,在编译时也可以不借助于Babel这样的工具;
二、TypeScript的编译环境:
全局安装TypeScript:npm install typescript -g(默认安装的就是最新版本)
查看我们安装的版本:tsc --version
运行TypeScript:
ts代码需要tsc编译TypeScript到JavaScript代码,然后在浏览器或者Node环境下运行JavaScript代码,每次运行显的优点麻烦,我们可以采取以下两种方式。
方式一:通过webpack,配置本地的TypeScript编译环境和开启一个本地服务,可以直接运行在浏览器上;
使用方法:https://mp.weixin.qq.com/s/wnL1l-ERjTDykWM76l4Ajw;
方式二:通过ts-node库,为TypeScript的运行提供执行环境;
三、TypeScript数据类型:
01_标识符的类型推导:
// 我们在声明一个标识符时, 如果有直接进行赋值, 会根据赋值的类型推导出标识符的类型注解,这个过程称之为类型推导
// let进行类型推导, 推导出来的通用类型
// const进行类型推导, 推导出来的字面量类型(后续专门讲解)
//name默认推导为string类型
let name = "why"
//age和height默认推导为number类型
let age = 18
const height = 1.88
// name = 123
export {}
02_JS中的类型Array:
// 明确的指定<数组>的类型注解: 有两种写法
// 1. string[]: 数组类型, 并且数组中存放的字符串类型
// 2. 泛型写法 Array<string>: 数组类型, 并且数组中存放的是字符串类型
// 数组一般存放相同的类型, 不要存放不同的类型
let names: string[] = ["aaa", "bbb", "ccc"]
names.push("ddd")
// names.push(123)不要存放不同类型数据
//nums是number[]类型
let nums: Array<number> = [123, 456, 789]
export {}
03_JS 中的类型Object:
object对象类型可以用于描述一个对象:
//尽量不这样写,当指定object类型时,此时object是一个空对象类型
const message: object = {
name:"jianghuai",
age:20,
height:1.80
}
message["name"]="ikun"
console.log(message["age"]);
但是从message中我们不能获取数据,也不能设置数据:
04_函数的类型:
// 在定义一个TypeScript中的函数时, 都要明确的指定参数的类型!
function sum(num1: number, num2: number) {
return num1 + num2
}
//返回值res也是number类型,自动进行类型推导
const res = sum(123, 321)
export { }
上面是自动进行返回值的类型推导,下面是明确进行类型指定
// 在定义一个TypeScript中的函数时
// 返回值类型可以明确的指定, 也可以自动进行类型推导
function sum(num1: number, num2: number): number {
return num1 + num2
}
const res = sum(123, 321)
export {}
小练习:
// 定义对象类型
type LyricType = {
time: number
text: string
}
// 歌词解析小demo,对返回值指定类型LyricType[]
function parseLyric(lyric: string): LyricType[] {
const lyrics: LyricType[] = []
lyrics.push({ time: 1111, text: "天空想要下雨" })
return lyrics
}
const lyricInfos = parseLyric("fdafdafdafa")
for (const item of lyricInfos) {
console.log(item.time, item.text)
}
export {}
05_匿名函数的类型:
const names: string[] = ["abc", "cba", "nba"]
// forEach内的function是匿名函数,匿名函数最好不要添加类型注解,会根据代码自动匹配类型。
//这个过程称之为上下文类型(contextual typing),因为函数执行的上下文可以帮助确定参数和返回值的类型;
names.forEach(function (item, index, arr) {
console.log(item, index, arr)
})
export { }
06_TS中的类型:
any类型:
1.对any类型的变量进行任何的操作,包括获取不存在的属性、方法;
2.给一个any类型的变量赋值任何的值,比如数字、字符串的值;
// any类型就表示不限制标识符的任意类型, 并且可以在该标识符上面进行任意的操作(在TypeScript中回到JavaScript中)
let id: any = "aaaa"
id = "bbbb"
id = 123
console.log(id.length)
id = { name: "why", level: 99 }
// 定义数组
const infos: any[] = ["abc", 123, {}, []]
什么时候用any类型?
如果对于某些情况的处理过于繁琐不希望添加规定的类型注解,或者在引入一些第三方库时,缺失了类型注解,这个时候我们可 以使用any:
包括在Vue源码中,也会使用到any来进行某些类型的适配;
unknow类型:
和any类型有点类似,但是unknown类型的值上做任何事情都是不合法的,想要操作必须得进行类型校验;
let foo: unknown = "aaa"
foo = 123
// unknown类型默认情况下在上面进行任意的操作都是非法的
// 要求必须进行类型的校验(缩小), 才能根据缩小之后的类型, 进行对应的操作
if (typeof foo === "string") { // 类型缩小
console.log(foo.length, foo.split(" "))
}
export {}
void类型:
// 1.在TS中如果一个函数没有任何的返回值, 那么返回值的类型就是void类型
// 2.如果返回值是void类型, 那么我们也可以返回undefined(TS编译器允许这样做而已)
function sum(num1: number, num2: number): void {
console.log(num1 + num2)
//return 123错误做法,因为明确指定了返回类型void
}
当基于上下文的类型推导(Contextual Typing)推导出返回类型为 void 的时候,并不会强制函数一定不能返回内容
const names = ["abc", "cba", "nba"]
// 了解即可: 基于上下文类型推导的函数中的返回值如果是void类型, 并且不强制要求不能返回任何的东西
names.forEach((item: string, index: number, arr: string[]) => {
console.log(item)
return 123
})
never类型:
never 表示永远不会发生值的类型,比如一个函数:
如果一个函数中是一个死循环或者抛出一个异常,那么这个函数会返回东西吗? 不会,那么写void类型或者其他类型作为返回值类型都不合适,我们就可以使用never类型;
// 二. 封装框架/工具库的时候可以使用一下never
// 其他时候在扩展工具的时候, 对于一些没有处理的case, 可以直接报错
function handleMessage(message: string | number | boolean) {
switch (typeof message) {
case "string":
console.log(message.length)
break
case "number":
console.log(message)
break
case "boolean":
console.log(Number(message))
break
default:
const check: never = message
}
}
handleMessage("aaaa")
handleMessage(1234)
// 调用这个函数,若传入了其他类型数据,对于一些没有处理的case,可以报错
handleMessage(true)
export {}
tuple类型(元组):
元组中每个元素都有自己特定的类型,根据索引值获取到的值可以确定对应的类型;
// 保存我的个人信息: why 18 1.88
// 1.使用数组类型
// 不合适: 数组中最好存放相同的数据类型, 获取值之后不能明确的知道对应的数据类型
const info1: any[] = ["why", 18, 1.88]
const value = info1[2]
console.log()
// 2.使用对象类型(最多)
const info2 = {
name: "why",
age: 18,
height: 1.88
}
// 3.使用元组类型
// 元组数据结构中可以存放不同的数据类型, 取出来的item也是有明确的类型
const info3: [string, number, number] = ["why", 18, 1.88]
const value2 = info3[2]
// 在函数中使用元组类型是最多的(函数的返回值)
function useState(initialState: number): [number, (newValue: number) => void] {
let stateValue = initialState
function setValue(newValue: number) {
stateValue = newValue
}
return [stateValue, setValue]
}
const [count, setCount] = useState(10)
console.log(count)
setCount(100)
export {}
四、TypeScript语法细讲 :
TS中联合类型的使用:
联合类型是由两个或者多个其他类型组成的类型,表示可以是这些类型中的任何一个值,联合类型中的每一个类型被称之为联合成员(union's members)
TS中的类型别名 :
在前面,我们通过在类型注解中编写 对象类型 和 联合类型,但是当我们想要多次在其他地方使用时,就要编写多次。 比如我们可以给对象类型起一个别名(type):
// 类型别名: type
type MyNumber = number
const age: MyNumber = 18
// 给ID的类型起一个别名
type IDType = number | string
function printID(id: IDType) {
console.log(id)
}
// 打印坐标
type PointType = { x: number, y: number, z?: number }
function printCoordinate(point: PointType) {
console.log(point.x, point.y, point.z)
}
TS中的接口声明使用:
在前面我们通过type可以用来声明一个对象类型:
对象的另外一种声明方式就是通过接口来声明:
那么它们有什么区别呢?
类型别名和接口非常相似,在定义对象类型时,大部分时候,你可以任意选择使用。 接口的几乎所有特性都可以在 type 中使用
// 区别一: type类型使用范围更广, 接口类型只能用来声明对象
type MyNumber = number
type IDType = number | string
// 区别二: 在声明对象时, interface可以多次声明
// 2.1. type不允许两个相同名称的别名同时存在!
// type PointType1 = {
// x: number
// y: number
// }
// type PointType1 = {
// z?: number
// }
// 2.2. interface可以多次声明同一个接口名称
interface PointType2 {
x: number
y: number
}
interface PointType2 {
z: number
}
const point: PointType2 = {
x: 100,
y: 200,
z: 300
}
// 区别三:interface支持继承的
interface IPerson {
name: string
age: number
}
interface IKun extends IPerson {
kouhao: string
}
const ikun1: IKun = {
kouhao: "你干嘛, 哎呦",
name: "kobe",
age: 30
}
// 区别四:interface可以被类实现(TS面向对象时候再讲)
// class Person implements IPerson {
// }
// 总结: 如果是非对象类型的定义使用type, 如果是对象类型的声明那么使用interface
export { }
TS中交叉类型的使用:
在开发中,我们进行交叉时,通常是对对象类型进行交叉的
// 回顾: 联合类型
type ID = number | string
const id1: ID = "abc"
const id2: ID = 123
// 交叉类型: 两种(多种)类型要同时满足
type NewType = number & string // 没有意义
interface IKun {
name: string
age: number
}
interface ICoder {
name: string
coding: () => void
}
type InfoType = IKun & ICoder
const info: InfoType = {
name: "why",
age: 18,
coding: function() {
console.log("coding")
}
}
TS中的类型断言as:
有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions)。
比如我们通过 document.getElementById,TypeScript只知道该函数会返回 HTMLElement ,但并不知道它具体的类型:
若这样,imgEl:Element | null,且imgEl的src,alt报错
使用类型断言:明确指定具体类型,则不报错
TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本,此规则可防止不可能的强制转换
TS中非空类型断言:
// 定义接口
interface IPerson {
name: string
age: number
friend?: {
name: string
}
}
const info: IPerson = {
name: "why",
age: 18
}
// 访问属性: 可选链: ?.
console.log(info.friend?.name)
// 属性赋值:
//赋值表达式的左侧不能是可选属性访问
//info.friend?.name="kk"
// 解决方案一: 类型缩小
if (info.friend) {
info.friend.name = "kobe"
}
// 解决方案二: 非空类型断言(有点危险, 只有确保friend一定有值的情况, 才能使用)
info.friend!.name = "james"
export {}
TS中的字面量类型的使用:
// 1.字面量类型的基本上
const name: "why" = "why"
let age: 18 = 18
// 2.将多个字面量类型联合起来 |
type Direction = "left" | "right" | "up" | "down"
const d1: Direction = "left"
// 栗子: 封装请求方法
type MethodType = "get" | "post"
function request(url: string, method: MethodType) {
}
request("http://codercba.com/api/aaa", "post")
// TS细节
// const info = {
// url: "xxxx",
// method: "post"
// }
// 下面的做法是错误: info.method获取的是string类型
// request(info.url, info.method)
// 解决方案一: info.method进行类型断言
// request(info.url, info.method as "post")
// 解决方案二: 直接让info对象类型是一个字面量类型
// const info2: { url: string, method: "post" } = {
// url: "xxxx",
// method: "post"
// }
const info2 = {
url: "xxxx",
method: "post"
} as const
//as const对整个对象进行字面量推理
// xxx 本身就是一个string
request(info2.url, info2.method)
export { }
TS中类型缩小的使用:
什么是类型缩小呢?
类型缩小的英文是 Type Narrowing,
我们可以通过类似于 typeof padding === "number" 的判断语句,来改变TypeScript的执行路径; 在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小( Narrowing ); 而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards);
常见的类型保护有如下几种:
typeof
平等缩小(比如===、!==)
instanceof
in
等等...
// 1.typeof: 使用的最多
function printID(id: number | string) {
if (typeof id === "string") {
console.log(id.length, id.split(" "))
} else {
console.log(id)
}
}
// 2.===/!==: 方向的类型判断
type Direction = "left" | "right" | "up" | "down"
function switchDirection(direction: Direction) {
if (direction === "left") {
console.log("左:", "角色向左移动")
} else if (direction === "right") {
console.log("右:", "角色向右移动")
} else if (direction === "up") {
console.log("上:", "角色向上移动")
} else if (direction === "down") {
console.log("下:", "角色向下移动")
}
}
// 3. instanceof: 传入一个日期, 打印日期
function printDate(date: string | Date) {
if (date instanceof Date) {
console.log(date.getTime())
} else {
console.log(date)
}
// if (typeof date === "string") {
// console.log(date)
// } else {
// console.log(date.getTime())
// }
}
// 4.in: 判断是否有某一个属性
interface ISwim {
swim: () => void
}
interface IRun {
run: () => void
}
function move(animal: ISwim | IRun) {
if ("swim" in animal) {
animal.swim()
} else if ("run" in animal) {
animal.run()
}
}
const fish: ISwim = {
swim: function() {}
}
const dog: IRun = {
run: function() {}
}
move(fish)
move(dog)
五、TypeScript函数类型
函数类型:
在JavaScript开发中,函数是重要的组成部分,并且函数可以作为参数,也可以作为返回值进行传递,那么在使用函数的过程中,函数是否也可以有自己的类型呢?
我们可以编写函数类型的表达式(Function Type Expressions),来表示函数类型;
在上面的语法中 (num1: number, num2: number) => void,代表的就是一个函数类型:
注意:在某些语言中,可能参数名称num1和num2是可以省略,但是TypeScript是不可以的
type CalcType = (num1: number, num2: number) => number
// 1.函数的定义
function calc(calcFn: CalcType) {
const num1 = 10
const num2 = 20
const res = calcFn(num1, num2)
console.log(res)
}
// 2.函数的调用
function sum(num1: number, num2: number) {
return num1 + num2
}
function foo(num1: number) {
//此处注意下个代码片段
return num1
}
calc(sum)
calc(foo)
function mul(num1: number, num2: number) {
return num1 * num2
}
calc(mul)
// 3.使用匿名函数
calc(function (num1, num2) {
return num1 - num2
})
export { }
// TypeScript对于传入的函数类型的多余的参数会被忽略掉(the extra arguments are simply ignored.)
type CalcType = (num1: number, num2: number) => number
function calc(calcFn: CalcType) {
calcFn(10, 20)
}
calc(function (num1) {
//注意此处
return 123
})
// forEach栗子:
const names = ["abc", "cba", "nba"]
names.forEach(function (item) {
console.log(item.length)
})
// TS对于很多类型的检测报不报错, 取决于它的内部规则
// TS版本在不断更新: 在进行合理的类型检测的情况, 让ts同时更好用(好用和类型检测之间找到一个平衡)
// 举一个例子:
interface IPerson {
name: string
age: number
}
// typescript github issue, 成员
const p = {
name: "why",
age: 18,
height: 1.88,
address: "广州市"
}
//通过p转换一下就没显示报错了
const info: IPerson = p
export { }
调用签名(Call Signatures):
在 JavaScript 中,函数除了可以被调用,自己也是可以有属性值的。
然而前面讲到的函数类型表达式并不能支持声明属性;
如果我们想描述一个带有属性的函数,我们可以在一个对象类型中写一个调用签名(call signature);
注意这个语法跟函数类型表达式稍有不同,在参数列表和返回的类型之间用的是 : 而不是 =>。
构造签名 :
JavaScript 函数也可以使用 new 操作符调用,当被调用的时候,TypeScript 会认为这是一个构造函数(constructors),因为 他们会产生一个新对象。
你可以写一个构造签名( Construct Signatures ),方法是在调用签名前面加一个 new 关键词;
函数的参数-可选参数:
可选类型需要在必传参数的后面:
函数的参数-默认参数:
// 函数的参数可以有默认值
// 1.有默认值的情况下, 参数的类型注解可以省略
// 2.有默认值的参数, 是可以接收一个undefined的值
function foo(x: number, y = 100) {
console.log(y + 10)
}
foo(10)
foo(10, undefined)
foo(10, 55)
export {}
函数的参数-剩余参数 :
从ES6开始,JavaScript也支持剩余参数,剩余参数语法允许我们将一个不定数量的参数放到一个数组中。
函数的重载:
在TypeScript中,如果我们编写了一个add函数,希望可以对字符串和数字类型进行相加,应该如何编写呢?
比如我们对sum函数进行重构:
在我们调用sum的时候,它会根据我们传入的参数类型来决定执行函数体时,到底执行哪一个函数的重载签名;
但是注意,有实现体的函数,是不能直接被调用的:
联合类型与重载:
我们现在有一个需求:定义一个函数,可以传入字符串或者数组,获取它们的长度。
这里有两种实现方案:方案一:使用联合类型来实现;方案二:实现函数重载来实现;
在可能的情况下,尽量选择使用联合类型来实现;
可推导的this类型:
简单掌握一些TypeScript中的this,TypeScript是如何处理this呢?我们先来看两个例子:
上面的代码默认情况下是可以正常运行的,也就是TypeScript在编译时,认为我们的this是可以正确去使用的:这是因为在没有指定this的情况,this默认情况下是any类型的;
VSCode在检测我们的TypeScript代码时,默认情况下运行不确定的this按照any类型去使用。
用 tsc --init 创建一个tsconfig.json文件,并且在其中告知VSCodethis必须明确执行(不能是隐式的);
在设置了noImplicitThis为true时, TypeScript会根据上下文推导this,但是在不能正确推导时,就会报错,需要我们明确 的指定this。
在开启noImplicitThis的情况下,我们必须指定this的类型。如何指定呢?
函数的第一个参数类型:
函数的第一个参数我们可以根据该函数之后被调用的情况,用于声明this的类型(名词必须叫this);
在后续调用函数传入参数时,从第二个参数开始传递的,this参数会在编译后被抹除;
this相关的内置工具:
Typescript 提供了一些工具类型来辅助进行常见的类型转换,这些类型全局可用。
function foo(this: { name: string }, info: {name: string}) {
console.log(this, info)
}
type FooType = typeof foo
// 1.ThisParameterType: 获取FooType类型中this的类型
type FooThisType = ThisParameterType<FooType>
// 2.OmitOmitThisParameter: 删除this参数类型, 剩余的函数类型
type PureFooType = OmitThisParameter<FooType>
// 3.ThisType: 用于绑定一个上下文的this
interface IState {
name: string
age: number
}
interface IStore {
state: IState
eating: () => void
running: () => void
}
const store: IStore & ThisType<IState> = {
state: {
name: "why",
age: 18
},
eating: function() {
console.log(this.name)
},
running: function() {
console.log(this.name)
}
}
store.eating.call(store.state)
export {}
六、TypeScript面向对象:
TS中类的基本使用:
只读属性readonly:
如果有一个属性我们不希望外界可以任意的修改,只希望确定值后直接使用,那么可以使用readonly:
class Person {
readonly name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
// 类和实例之间的关系(重要)
const p = new Person("why", 18)
console.log(p.name, p.age)
// p.name = "kobe" 只读属性不能进行写入操作
p.age = 20
export {}
getters/setters :
在前面一些私有属性我们是不能直接访问的,或者某些属性我们想要监听它的获取(getter)和设置(setter)的过程,这个时候我们 可以使用存取器。
class Person {
// 私有属性: 属性前面会使用_
private _name: string
private _age: number
constructor(name: string, age: number) {
this._name = name
this._age = age
}
running() {
console.log("running:", this._name)
}
// setter/getter: 对属性的访问进行拦截操作
set name(newValue: string) {
this._name = newValue
}
get name() {
return this._name
}
set age(newValue: number) {
if (newValue >= 0 && newValue < 200) {
this._age = newValue
}
}
get age() {
return this._age
}
}
const p = new Person("why", 100)
p.name = "kobe"
console.log(p.name)
p.age = -10
console.log(p.age)
export {}
参数属性(Parameter Properties):
class Person {
// 语法糖
constructor(public name: string, private _age: number, readonly height: number) {
}
running() {
console.log(this._age, "eating")
}
}
const p = new Person("why", 18, 1.88)
console.log(p.name, p.height)
// p.height = 1.98
export {}
抽象类abstract:
abstract class Shape {
// getArea方法只有声明没有实现体
// 实现让子类自己实现
// 可以将getArea方法定义为抽象方法: 在方法的前面加abstract
// 抽象方法必须出现在抽象类中, 类前面也需要加abstract
abstract getArea()
}
class Rectangle extends Shape {
constructor(public width: number, public height: number) {
super()
}
getArea() {
return this.width * this.height
}
}
class Circle extends Shape {
constructor(public radius: number) {
super()
}
getArea() {
return this.radius ** 2 * Math.PI
}
}
class Triangle extends Shape {
getArea() {
return 100
}
}
// 通用的函数
function calcArea(shape: Shape) {
return shape.getArea()
}
calcArea(new Rectangle(10, 20))
calcArea(new Circle(5))
calcArea(new Triangle())
// 在Java中会报错: 不允许
calcArea({ getArea: function() {} })
// 抽象类不能被实例化
// calcArea(new Shape())
// calcArea(100)
// calcArea("abc")
类型检测-鸭子类型:
// TypeScript对于类型检测的时候使用的鸭子类型
// 鸭子类型: 如果一只鸟, 走起来像鸭子, 游起来像鸭子, 看起来像鸭子, 那么你可以认为它就是一只鸭子
// 鸭子类型, 只关心属性和行为, 不关心你具体是不是对应的类型
class Person {
constructor(public name: string, public age: number) { }
running() { }
}
class Dog {
constructor(public name: string, public age: number) { }
running() { }
}
function printPerson(p: Person) {
console.log(p.name, p.age)
}
printPerson(new Person("why", 18))
// printPerson("abc")
printPerson({ name: "kobe", age: 30, running: function () { } })//直接写字面量
printPerson(new Dog("旺财", 3))//创建个小狗
const person: Person = new Dog("果汁", 5)//这也没报错
export { }
类本身也是可以作为一种数据类型的:
class Person { }
/**
* 类的作用:
* 1.可以创建类对应的实例对象
* 2.类本身可以作为这个实例的类型
* 3.类也可以当做有一个构造签名的函数
*/
const name: string = "aaa"
const p: Person = new Person()
function printPerson(p: Person) { }
function factory(ctor: new () => void) { }
factory(Person)//直接传入Person类
export { }
索引签名:
接口的继承和实现:
interface IPerson {
name: string
age: number
}
// 可以从其他的接口中继承过来属性
// 1.减少了相同代码的重复编写
// 2.如果使用第三库, 给我们定义了一些属性
// > 自定义一个接口, 同时你希望自定义接口拥有第三方某一个类型中所有的属性
// > 可以使用继承来完成
interface IKun extends IPerson {
slogan: string
}
const ikun: IKun = {
name: "why",
age: 18,
slogan: "你干嘛, 哎呦"
}
export {}
接口可以被继承也可以被类实现: 通过类创建的实例都会具备接口的特性
interface IKun {
name: string
age: number
slogan: string
playBasketball: () => void
}
interface IRun {
running: () => void
}
const ikun: IKun = {
name: "why",
age: 18,
slogan: "你干嘛!",
playBasketball: function () { }
}
// 作用: 接口被类实现,接口中的所有类都要具备
class Person implements IKun, IRun {
name: string
age: number
slogan: string
playBasketball() {
}
running() {
}
}
const ikun2 = new Person()
const ikun3 = new Person()
const ikun4 = new Person()
console.log(ikun2.name, ikun2.age, ikun2.slogan)
ikun2.playBasketball()
ikun2.running()
抽象类和接口的区别(了解):
严格的字面量赋值检测:
interface IPerson {
name: string
age: number
}
// 1.奇怪的现象一:
// 定义info, 类型是IPerson类型
const obj = {
name: "why",
age: 18,
// 多了一个height属性
height: 1.88
}
const info: IPerson = obj//不会报错,通过中间量obj传递
//const info1:IPerson={name:"kobe",age:18,height:1.88}会报错,height报错
// 2.奇怪的现象二:
//直接调用函数并传入属性值也是会报错,但是通过中间量kobe传入调用则不会报错
function printPerson(person: IPerson) {
}
const kobe = { name: "kobe", age: 30, height: 1.98 }
printPerson(kobe)
// 解释现象
// 第一次创建的对象字面量, 称之为fresh(新鲜的)
// 对于新鲜的字面量, 会进行严格的类型检测. 必须完全满足类型的要求(不能有多余的属性)
const obj2 = {
name: "why",
age: 18,
height: 1.88
}
const p: IPerson = obj2
export { }
TypeScript枚举类型:
// 定义枚举类型
enum Direction {
LEFT,
RIGHT
}
const d1: Direction = Direction.LEFT
function turnDirection(direction: Direction) {
switch(direction) {
case Direction.LEFT:
console.log("角色向左移动一个格子")
break
case Direction.RIGHT:
console.log("角色向右移动一个格子")
break
}
}
// 监听键盘的点击
turnDirection(Direction.LEFT)
export {}