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 {}

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江淮-Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值