Typescript语法

Typescript: 进入类型的世界

编程语言的类型:

动态类型语言(Dynamically Typed Language)

动态类型语言是指在运行期间才去做数据类型检查的语言。也就是说,在用动态类型的语言编程时,永远不用给任何变量指定数据类型,该语言会在你第一次赋值给变量的时候,在内部将数据类型记录下来。Python和Ruby就是一种典型的动态类型语言,其它的各种脚本语言如VBScript也多少属于动态类型语言。

静态类型语言(Statically Typed Language)

静态类型语言与动态类型语言刚好相反,它的数据类型是在编译期间检查的。也就是说,在编写程序的时候就要声明所有变量的数据类型。C/C++是静态类型语言的典型带便,其它的静态类型语言还有C#、Java等。

TypeScript是什么?

  • Typed JavaScript at Any Scale
  • 静态类型风格的类型系统

  • 从es6到es10甚至是esnext的语法支持

  • 兼容各种浏览器,各种系统,各种服务器,完全开源

TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。

为什么要学习 typescript

程序更容易理解

  • ​ 问题:函数或者方法输入输出的参数类型,外部条件等
  • ​ 动态语言的约束:需要手动调试等过程
  • ​ 有了Typescript:代码本身就可以回答上诉问题

效率更高

  • ​ 在不同的代码块和定义中跳转
  • ​ 代码自动补全

更少的错误

  • ​ 编译期间能发现大部分错误
  • ​ 杜绝一些比较常见的错误

非常好的包容性

  • ​ 完全兼容Javascript
  • ​ 第三方库可以单独编写类型文件

一些小缺点:

  • ​ 增加了一些学习成本
  • ​ 短期内增加了一些开发成本

安装 typescript:

确定安装好node、npm版本 推荐node 10以上版本

有两种主要的方式来获取TypeScript工具:

  • 通过npm(Node.js包管理器)
  • 安装Visual Studio的TypeScript插件

Visual Studio 2017和Visual Studio 2015 Update 3默认包含了TypeScript。 如果你的Visual Studio还没有安装TypeScript,你可以下载它。

Typescript 官网地址: https://www.typescriptlang.org/zh/

使用 nvm 来管理 node 版本: https://github.com/nvm-sh/nvm

针对使用npm的用户:

> cnpm install -g typescript

使用 tsc 全局命令:

// 查看 tsc 版本
tsc -v
// 编译 ts 文件
tsc fileName.ts

原始数据类型和 Any 类型

Typescript 文档地址:Basic Types

Javascript 类型分类:

最新的 ECMAScript 标准定义了 8 种数据类型:

  • 7 种

    原始数据类型- primitive values:

    • Boolean
    • Null
    • Undefined
    • Number
    • BigInt
    • String
    • Symbol
  • 和 Object

原始值( primitive values )

除 Object 以外的所有类型都是不可变的(值本身无法被改变)

let isDone: boolean = false

// 接下来来到 number,注意 es6 还支持2进制和8进制,让我们来感受下

let age: number = 10
let binaryNumber: number = 0b1111

// 之后是字符串,注意es6新增的模版字符串也是没有问题的
let firstName: string = 'viking'
let message: string = `Hello, ${firstName}, age is ${age}`

// 还有就是两个奇葩兄弟两,undefined 和 null
let u: undefined = undefined
let n: null = null

// 注意 undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:
let num: number = undefined

any 类型

let notSure: any = 4
notSure = 'maybe it is a string'
notSure = 'boolean'
// 在任意值上访问任何属性都是允许的:
notSure.myName
// 也允许调用任何方法:
notSure.getName()

Array 和 Tuple(元组 ):

Typescript 文档地址:Array 和 Tuple

数组讲同一类型的数据聚合在一起:

数组如果要聚合不同类型的数据聚合在一起:使用any会丧失数据类型 达不到要求

元组起源于函数式编程:

let users: [string,number]= [‘hello’,20] //元组

//最简单的方法是使用「类型 + 方括号」来表示数组:
let arrOfNumbers: number[] = [1, 2, 3, 4]
//数组的项中不允许出现其他的类型:
//数组的一些方法的参数也会根据数组在定义时约定的类型进行限制:
arrOfNumbers.push(3)
arrOfNumbers.push('abc')

// 元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用
let user: [string, number] = ['viking', 20]
//但是当我们写少一项 就会报错 同样写多一项也会有问题
user = ['molly', 20, true]

Interface-接口

Typescript 文档地址:Interface

Duck Typing 概念:

如果某个东西长得像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那它就可以被看成是一只鸭子。

  • ​ 对对象的形状(shape)进行描述–规则
  • ​ Duck Typing(鸭子类型)
// 我们定义了一个接口 Person
interface Person {
  name: string;
  age: number;
}
// 接着定义了一个变量 viking,它的类型是 Person。这样,我们就约束了 viking 的形状必须和接口 Person 一致。
let viking: Person ={
  name: 'viking',
  age: 20
}

//有时我们希望不要完全匹配一个形状,那么可以用可选属性:
interface Person {
    name: string;
    age?: number;
}
let viking: Person = {
    name: 'Viking'
}

//接下来还有只读属性,有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性

interface Person {
  readonly id: number;
  name: string;
  age?: number;
}
viking.id = 9527

函数

Typescript 文档地址:Functions

在JS中,函数是一等公民

// 来到我们的第一个例子,约定输入,约定输出
function add(x: number, y: number): number {
  return x + y
}
// 可选参数
function add(x: number, y: number, z?: number): number {
  if (typeof z === 'number') {
    return x + y + z
  } else {
    return x + y
  }
}

// 函数本身的类型
const add2: (x: number, y: number, z?:number) => number = add

// interface 描述函数类型
const sum = (x: number, y: number) => {
  return x + y
}
interface ISum {
  (x: number, y: number): number
}
const sum2: ISum = sum
 	

类型推论 联合类型和 类型断言

Typescript 文档地址:类型推论 - type inference

联合类型 - union types

//type inference
let str = "str"
// str = 123

// 我们只需要用中竖线来分割两个
let numberOrString: number | string 
// 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:
numberOrString.length
numberOrString.toString()

类型断言 - type assertions

// 这里我们可以用 as 关键字,告诉typescript 编译器,你没法判断我的代码,但是我本人很清楚,这里我就把它看作是一个 string,你可以给他用 string 的方法。
function getLength(input: string | number): number {
  const str = input as string
  if (str.length) {
    return str.length
  } else {
    const number = input as number
    return number.toString().length
  }
}

类型守卫 - type guard

// typescript 在不同的条件分支里面,智能的缩小了范围,这样我们代码出错的几率就大大的降低了。
function getLength2(input: string | number): number {
  if (typeof input === 'string') {
    return input.length
  } else {
    return input.toString().length
  }
}

Class 类

面向对象编程(OOP)的三大特点

  • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性。
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。

类(class) -定义了一切事物的抽象特点

​ 对象(object):类的实例

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name
  }
  run() {
    return `${this.name} is running`
  }
}
const snake = new Animal('lily')

// 继承的特性
class Dog extends Animal {
  bark() {
    return `${this.name} is barking`
  }
}

const xiaobao = new Dog('xiaobao')
console.log(xiaobao.run())
console.log(xiaobao.bark())

// 这里我们重写构造函数,注意在子类的构造函数中,必须使用 super 调用父类的方法,要不就会报错。
class Cat extends Animal {
  constructor(name) {
    super(name)
    console.log(this.name)
  }
  run() {
    return 'Meow, ' + super.run()
  }
}
const maomao = new Cat('maomao')
console.log(maomao.run())

类成员的访问修饰符

  • ​ public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的

  • ​ private 修饰的属性或方法是私有的,不能在声明它的类的外部访问

  • ​ protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

类和接口 - 完美搭档

​ 继承的困境 extends 单继承 只能有一个父类

​ 类可以使用implements来实现接口 实现多接口 有多个父接口

类实现一个接口



interface Radio {
  switchRadio(trigger: boolean): void;
}
class Car implements Radio {
  switchRadio(trigger) {
    return 123
  }
}
class Cellphone implements Radio {
  switchRadio() {
  }
}

interface Battery {
  checkBatteryStatus(): void;
}

// 要实现多个接口,我们只需要中间用 逗号 隔开即可。
class Cellphone implements Radio, Battery {
  switchRadio() {
  }
  checkBatteryStatus() {

  }
}

枚举 Enums

枚举 Enums

​ 常量: 固定的值

​ 枚举:有些取值是在一定范围内的值:比如周一至周日 rgb 上下左右 就适合用枚举表示

​ 枚举的值有两种类型: 常量值(const number)、计算值(computed number)

​ 只有常量值才可以常量枚举

//数字枚举,一个数字枚举可以用 enum 这个关键词来定义,我们定义一系列的方向,然后这里面的值,枚举成员会被赋值为从 0 开始递增的数字,
enum Direction {
  Up,
  Down,
  Left,
  Right,
}
console.log(Direction.Up)

// 还有一个神奇的点是这个枚举还做了反向映射
console.log(Direction[0])


// 字符串枚举
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT',
}
const value = 'UP'
if (value === Direction.Up) {
  console.log('go up!')
}

泛型 Generics

泛型 Generics

泛型(Generics) 第一部分

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function echo(arg) {
  return arg
}
const result = echo(123)
// 这时候我们发现了一个问题,我们传入了数字,但是返回了 any

function echo(arg: T): T {
  return arg
}
const result = echo(123)

// 泛型也可以传入多个值
function swap(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]]
}

const result = swap(['string', 123])

泛型就是在定义函数、接口、类的时候,先不具体指定具体类型,而在使用的时候指定类型和特征。

泛型像占位符,使用时候把数据类型传入。

泛型(Generics) 第二部分 - 约束泛型

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法


function echoWithArr(arg: T): T {
  console.log(arg.length)
  return arg
}

// 上例中,泛型 T 不一定包含属性 length,我们可以给他传入任意类型,当然有些不包括 length 属性,那样就会报错

//泛型约束
function echoWithArr(arg: T[]): T[]{
    console.log(arg.length)
    return arg
}

const arrs = echoWithArr([1,2,3])
// const arrs = echoWithArr('str')

//只传入包含length属性的数据类型
interface IWithLength {
  length: number;
}

function echoWithLength(arg: T): T {
  console.log(arg.length)
  return arg
}

const str1 = echoWithLength('str')
const obj = echoWithLength({length: 10,width: 10})
const arr2 = echoWithLength([1,2,3])
// echoWithLength(13)   //报错

泛型第三部分 - 泛型在类和接口中的使用

class Queue {
  private data = [];
  push(item) {
    return this.data.push(item)
  }
  pop() {
    return this.data.shift()
  }
}

const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed())

//在上述代码中存在一个问题,它允许你向队列中添加任何类型的数据,当然,当数据被弹出队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据,但是那么在使用的过程中,就会出现我们无法捕捉到的错误,


class  Queue {
    private  data = []
    push(item: T){
        return this.data.push(item)
    }
    pop(): T{
        return this.data.shift()
    }
}

const queue = new Queue()
queue.push(1)
// queue.push('str')
console.log(queue.pop().toFixed())//  
console.log(queue.pop().toFixed())//

interface   KeyPair{
    key: T
    value: U
}

let kp1:KeyPair = {key: 1,value: "string"}
let kp2:KeyPair = {key: 'str',value: 2}
//定义数组形式
let arr: number[] = [1,2,3]
//可以泛型表示
let arrTwo: Array = [1,2,3]

​ 泛型相当于1:创建特定类型的一个容器、2:灵活的约束参数的类型、3:函数使用时,类型推断不会流入到函数体内,使用泛型能达到流入函数体内的效果。

类型别名,字面量 和 交叉类型:


// 类型别名  type aliase
let sum: (x: number, y: number) => number
const result4 = sum(1,2)
type PlusType = (x: number, y: number) => number
let sum2: PlusType
const result5 = sum2(2,3)//只是演示 没有实际功能 运行报错

type StrOrNumber = string | number
let result6: StrOrNumber = '123'
result6 = 123

//字面量  字符串字面量  数字字面量
const str: 'name' = 'name'
const number: 1 = 1
// 字符串字面量
type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Directions = 'Left'


interface IName {
    name: string
}

//交叉类型
type IPerson = IName & {age: number}
let person: IPerson = {name:'123',age:123}

类型别名 和 交叉类型

类型别名 Type Aliases

类型别名,就是给类型起一个别名,让它可以更方便的被重用。

let sum: (x: number, y: number) => number
const result = sum(1,2)
type PlusType = (x: number, y: number) => number
let sum2: PlusType

// 支持联合类型
type StrOrNumber = string | number
let result2: StrOrNumber = '123'
result2 = 123

// 字符串字面量
type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Directions = 'Up'

交叉类型 Intersection Types

interface IName  {
  name: string
}
type IPerson = IName & { age: number }
let person: IPerson = { name: 'hello', age: 12}

声明文件

声明文件

@types 官方声明文件库 @types 搜索声明库

	//使用第三方库时  怎样创建申明文件  怎样快速安装申明文件

	jQuery('#foo')

	//只有编译时检查  没有功能

    declare var jQuery: (selector:string)=> any;

	//直接用npm写好的   npm install --save @types/jquery

内置类型

内置类型

内置对象:在全局作用域上(global)的对象。 安装环境就已经内置好了。

const a: Array = [1,2,3]
// 大家可以看到这个类型,不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧
const date: Date = new Date()
const reg = /abc/
// 我们还可以使用一些 build in object,内置对象,比如 Math 与其他全局对象不同的是,Math 不是一个构造器。Math 的所有属性与方法都是静态的。

Math.pow(2,2)

// DOM 和 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

Typescript 还提供了一些功能性,帮助性的类型,这些类型,大家在 js 的世界是看不到的,这些类型叫做 utility types,提供一些简洁明快而且非常方便的功能。


//Utility Types

interface IPerson {
    name: string 
    age: number
}

let tom: IPerson = {name: 'tom',age: 20}
type IPartial = Partial
let tom2: IPartial = { name: 'tom2'}
type IOmit = Omit
let tom3: IOmit = {age:20}


//global objects
const a: Array = [1,2,3]
const date = new Date()
date.getTime()
const reg = /abc/
reg.test('abc')

//build-in object
Math.pow(2,2)

//DOM  and BOM
let body = document.body
let alllis = document.querySelectorAll('li')
alllis.keys()

document.addEventListener('click',(e)=>{
    e.preventDefault()
})

//Utility Types

interface IPerson {
    name: string 
    age: number
}

let tom: IPerson = {name: 'tom',age: 20}
type IPartial = Partial
let tom2: IPartial = { name: 'tom2'}
type IOmit = Omit
let tom3: IOmit = {age:20}

你可能感兴趣的:(前端知识体系,主流面试题,vue3,typescript,前端,javascript)