> 文章列表 > TypeScript由浅到深(上篇)

TypeScript由浅到深(上篇)

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 = 123export {}

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: numbertext: 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)breakcase "number":console.log(message)breakcase "boolean":console.log(Number(message))breakdefault: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 = initialStatefunction 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 = numberconst age: MyNumber = 18// 给ID的类型起一个别名
type IDType = number | stringfunction 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: numbery: number
}interface PointType2 {z: number
}const point: PointType2 = {x: 100,y: 200,z: 300
}// 区别三:interface支持继承的
interface IPerson {name: stringage: number
}interface IKun extends IPerson {kouhao: string
}const ikun1: IKun = {kouhao: "你干嘛, 哎呦",name: "kobe",age: 30
}// 区别四:interface可以被类实现(TS面向对象时候再讲)
// class Person implements IPerson {
// }// 总结: 如果是非对象类型的定义使用type, 如果是对象类型的声明那么使用interfaceexport { }

TS中交叉类型的使用:

在开发中,我们进行交叉时,通常是对对象类型进行交叉的

// 回顾: 联合类型
type ID = number | string
const id1: ID = "abc"
const id2: ID = 123// 交叉类型: 两种(多种)类型要同时满足
type NewType = number & string // 没有意义interface IKun {name: stringage: number
}interface ICoder {name: stringcoding: () => void
}type InfoType = IKun & ICoderconst 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: stringage: numberfriend?: {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 = 10const num2 = 20const 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: stringage: number
}// typescript github issue, 成员
const p = {name: "why",age: 18,height: 1.88,address: "广州市"
}
//通过p转换一下就没显示报错了
const info: IPerson = pexport { }

调用签名(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: stringage: number
}interface IStore {state: IStateeating: () => voidrunning: () => 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: stringage: numberconstructor(name: string, age: number) {this.name = namethis.age = age}
}// 类和实例之间的关系(重要)
const p = new Person("why", 18)
console.log(p.name, p.age)// p.name = "kobe" 只读属性不能进行写入操作
p.age = 20export {}

getters/setters :

        在前面一些私有属性我们是不能直接访问的,或者某些属性我们想要监听它的获取(getter)和设置(setter)的过程,这个时候我们 可以使用存取器。

class Person {// 私有属性: 属性前面会使用_private _name: stringprivate _age: numberconstructor(name: string, age: number) {this._name = namethis._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.98export {}

抽象类abstract:

abstract class Shape {// getArea方法只有声明没有实现体// 实现让子类自己实现// 可以将getArea方法定义为抽象方法: 在方法的前面加abstract// 抽象方法必须出现在抽象类中, 类前面也需要加abstractabstract 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: stringage: number
}// 可以从其他的接口中继承过来属性
// 1.减少了相同代码的重复编写
// 2.如果使用第三库, 给我们定义了一些属性
//  > 自定义一个接口, 同时你希望自定义接口拥有第三方某一个类型中所有的属性
//  > 可以使用继承来完成
interface IKun extends IPerson {slogan: string
}const ikun: IKun = {name: "why",age: 18,slogan: "你干嘛, 哎呦"
}export {}

接口可以被继承也可以被类实现: 通过类创建的实例都会具备接口的特性

interface IKun {name: stringage: numberslogan: stringplayBasketball: () => void
}interface IRun {running: () => void
}const ikun: IKun = {name: "why",age: 18,slogan: "你干嘛!",playBasketball: function () { }
}// 作用: 接口被类实现,接口中的所有类都要具备
class Person implements IKun, IRun {name: stringage: numberslogan: stringplayBasketball() {}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: stringage: 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 = obj2export { }

 TypeScript枚举类型:

// 定义枚举类型
enum Direction {LEFT,RIGHT
}const d1: Direction = Direction.LEFTfunction turnDirection(direction: Direction) {switch(direction) {case Direction.LEFT:console.log("角色向左移动一个格子")breakcase Direction.RIGHT:console.log("角色向右移动一个格子")break}
}// 监听键盘的点击
turnDirection(Direction.LEFT)export {}

 

 

 

为何网