TypeScript 介绍

什么是 TypeScript

是 JavaScript 的一个超集,它能够编译成纯 JavaScript。编译进去的 JavaScript 能够运行在任何浏览器上,次要提供了类型零碎对 ES6 的反对

为什么抉择 TypeScript

  • 减少了代码的可维护性
  • 包容性强,反对 ES6 语法,.js文件间接重命名为.ts即可
  • 兼容第三方库,即便第三方库不是 TypeScript 写的,也能够通过独自编写类型文件供辨认读取
  • 社区沉闷,目前三大框架还有越来越多的库都反对 TypeScript 了

TypeScript 只会在编译的时候对类型进行动态查看,如果发现有谬误,编译的时候就会报错。

装置 TypeScript

  1. 全局装置 TS
npm i -g typescript
  1. 查看 TypeScript 版本号
tsc -v

我以后版本为 Version 4.0.2

  1. 初始化生成 tsconfig.json 文件
tsc --init
  1. 在 tsconfig.json 中设置源代码目录和编译生成 js 文件的目录
"outDir": "./dist","rootDir": "./src"
  1. 监听 ts 文件的变动,每当文件产生扭转就主动编译
tsc -w

之后你写的 ts 文件编译谬误都会间接提醒,如果想运行文件,就到 /dist目录下找相应的 js 文件,应用 node 运行即可

ts-node 装置

当然这样其实也挺麻烦,咱们想间接运行 TS 文件, 这时能够借助ts-node插件

全局装置

npm install -g ts-node

找到文件门路,运行即可

ts-node demo.ts

根底类型

Number 类型

let num: number = 2

Boolean 类型

let isShow: boolean = true

String 类型

let str: string = 'hello'

Array 类型

let arr1: number[] = [1, 2, 3]let arr2: Array<number> = [2, 3, 4]

Any 类型

let foo: any = 'hello'foo = 12foo = false

Null 和 Undefined 类型

null 和 undefined 能够赋值给任意类型的变量

let test1: undefined = undefinedlet test2: null = nulllet test3: numberlet test4: stringtest3 = nulltest4 = undefined

Void 类型

void 类型像是与 any 类型相同,它示意没有任何类型。当一个函数没有返回值时,其返回值类型是 void

let test5: void = undefined // 申明一个 void 类型的变量没有什么用,因为它的值只能为 undefined 或 nullfunction testFunc(): void {} // 函数没有返回值

Never 类型

never 类型示意的是那些永不存在的值的类型。

function bar(): never {  throw new Error('never reach')}

Unknown 类型

所有类型都能够赋值给 any,所有类型也都能够赋值给 unknown。

let value: unknownvalue = 123value = 'Hello'value = truelet value1: unknown = valuelet value2: any = valuelet value3: boolean = value // Errorlet value4: number = value // Errorlet value5: string = value // Errorlet value6: object = value // Errorlet value7: any[] = value // Errorlet value8: Function = value // Error

unknown 类型只能被赋值给 any 类型和 unknown 类型自身。

Tuple 类型

数组合并了雷同类型的对象,而元组(Tuple)合并了不同类型的对象。元组示意一个数量和类型都已知的数组

let tupleArr1: [string, number] = ['hello', 10]// let tupleArr2: [string, number] = [10, 'hello'] // Error

Enum 类型

应用枚举能够定义一些带名字的常量。 TypeScript 反对数字的和基于字符串的枚举。

  enum Season {    Spring,    Summer,    Autumn,    Winter,  }  let a: Season = Season.Spring  let b: Season = Season.Summer  let c: Season = Season.Autumn  let d: Season = Season.Winter  console.log(a, b, c, d) // 0 1 2 3}

函数类型

函数申明

// JSfunction func1(x, y) {  return x + y}// TSfunction func2(x: number, y: number): number {  return x + y}

函数表达式

在 TypeScript 的类型定义中,=> 用来示意函数的定义,右边是输出类型,须要用括号括起来,左边是输入类型。

// JSlet func3 = function (x, y) {  return x + y}// TS 第一种形式let func4 = function (x: number, y: number): number {  return x + y}// TS 第二种形式// => 用来示意函数的定义,右边是输出类型,须要用括号括起来,左边是输入类型。let func5: (x: number, y: number) => number = function (x: number, y: number): number {  return x + y}

接口定义函数的形态

采纳函数表达式|接口定义函数的形式时,对等号左侧进行类型限度,能够保障当前对函数名赋值时保障参数个数、参数类型、返回值类型不变。

interface SearchFunc {  (source: string, subString: string): boolean}let mySearch: SearchFuncmySearch = function (source: string, subString: string) {  return source.search(subString) !== -1}

可选参数

对于 TS 的函数,输出多余的(或者少于要求的)参数,是不容许的,那么该怎么设置可选参数呢?,咱们能够在申明之后加一个问号

function getName1(firstName: string, lastName?: string) {  // 可选参数必须接在必须参数前面  if (lastName) {    return `${firstName} ${lastName}`  } else {    return firstName  }}let name1 = getName1('jacky')let name2 = getName1('jacky', 'lin')console.log(name1, name2)

参数默认值

function getName2(firstName: string = 'monkey', lastName: string) {  return `${firstName} ${lastName}`}console.log(getName2('jacky', 'Lin'))console.log(getName2(undefined, 'Lin')) // monkey Lin

void 和 never 类型

当函数没有返回值时,能够用 void 来示意。

当一个函数永远不会返回时,咱们能够申明返回值类型为 never

function func6(): void {  // return null}function func7(): never {  throw new Error('never reach')}

残余参数

function push1(arr, ...items) {  items.forEach(function (item) {    arr.push(item)  })}let a: any[] = []push1(a, 1, 2, 3)function push2(arr: any[], ...items: any[]): void {  items.forEach(function (item) {    arr.push(item)  })}let b: any[] = []push1(b, 1, 2, 3, '5')

函数参数为对象(解构)时

// js写法function add({ one, two }) {  return one + two}const total = add({ one: 1, two: 2 })// ts 写法function add1({ one, two }: { one: number; two: number }): number {  return one + two}const three = add1({ one: 1, two: 2 })

函数重载

重载容许一个函数承受不同数量或类型的参数时,作出不同的解决。

function reverse(x: number): number // 函数定义function reverse(x: string): string // 函数定义function reverse(x: number | string): number | string {  // 函数实现  if (typeof x === 'number') {    return Number(x.toString().split('').reverse().join(''))  } else if (typeof x === 'string') {    return x.split('').reverse().join('')  }}

类型断言

TypeScript 容许你笼罩它的推断,并且能以你任何你想要的形式剖析它,这种机制被称为「类型断言」。

TypeScript 类型断言用来通知编译器你比它更理解这个类型,你晓得你本人在干什么,并且它不应该再收回谬误。

interface Cat {  name: string  run(): void}interface Fish {  name: string  swim(): void}// 只能拜访共有的属性function getName(animal: Cat | Fish): string {  return animal.name}// 将 animal 断言成 Fish 就能够解决拜访 animal.swim 时报错的问题function isFish(animal: Cat | Fish): boolean {  if (typeof (animal as Fish).swim === 'function') {    return true  }  return false}// 任何类型都能够被断言为 any;(window as any).randomFoo = 1
// 类型断言第一种形式:"尖括号"语法let value1: any = 'hello'let value1Length: number = (<string>value1).length// 类型断言第二种形式:aslet value2: any = 'world'let value2Length: number = (value2 as string).length

存取器

class Animal {  constructor(name: string) {    this.name = name  }  // getter  get name() {    return '名字'  }  // setter  set name(value: string) {    console.log('setter: ' + value)  }}let animal = new Animal('monkey')console.log(animal.name) // monkeyanimal.name = 'mk' // setter: mk

拜访修饰符

  • public 润饰的属性或办法是私有的,能够在任何中央被拜访到,默认所有的属性和办法都是 public 的
  • private 润饰的属性或办法是公有的,不能在申明它的类的内部拜访
  • protected 润饰的属性或办法是受爱护的,它和 private 相似,区别是它在子类中也是容许被拜访的
class Person {  public name  private age  protected sex  public constructor(name: string, age: number, sex: string) {    this.name = name    this.age = age    this.sex = sex  }}let person1 = new Person('jacky', 22, 'man')person1.name = 'monkey' // name值能够拜访且批改// person1.age // Property 'age' is private and only accessible within class 'Person'.// person1.sex // Property 'sex' is private and only accessible within class 'Person'.class Person1 extends Person {  constructor(name: string, age: number, sex: string) {    super(name, age, sex)    // console.log(this.name, this.age, this.sex) // Property 'age' is private and only accessible within class 'Person'.  }}

参数属性

同时给类中定义属性的同时赋值

class Animal3 {  public name  constructor(name: string) {    this.name = name  }}console.log(new Animal3('animal3').name) // animal3class Animal2 {  constructor(public name: string) {} // 简洁模式}console.log(new Animal2('animal2').name) // animal2
  • readonly 只读属性
class Animal4 {  readonly name  constructor(name: string) {    this.name = name  }}let animal4 = new Animal4('animal4')// animal4.name = '5' // Cannot assign to 'name' because it is a read-only property

抽象类

// 抽象类是行为的形象,个别来封装公共属性的办法的,不能被实例化abstract class CommonAnimal {  name: string  abstract speak(): void}

static

class Animal5 {  static sayHi() {    console.log('Hello Animal5')  }}Animal5.sayHi()

联结类型

联结类型(Union Types)示意取值能够为多种类型中的一种。应用一个|宰割符来宰割多种类型

let foo: string | number | booleanfoo = 'test'foo = 3foo = true

穿插类型

interface IPerson {  id: string  age: number}interface IWorker {  companyId: string}type IStaff = IPerson & IWorkerconst staff: IStaff = { id: '007', age: 24, companyId: '1' }

类型别名

type Message = string | string[]let getMsg = (message: Message) => {  return message}type Weather = 'SPRING' | 'SUMMER' | 'AUTUMN' | 'WINTER'let weather1: Weather = 'SPRING'let weather2: Weather = 'AUTUMN'

接口

对象的形态

interface Person1 {  name: string  age: number}let person1: Person1 = {  name: 'jacky',  age: 23,}

形容行为的形象

interface AnimalLike {  eat(): void  move(): void}interface PersonLike extends AnimalLike {  speak(): void}class Human implements PersonLike {  speak() {}  eat() {}  move() {}}

含构建函数作参数的写法

class Animal1 {  constructor(public name: string) {}  age: number}class Animal2 {  constructor(public age: number) {}}interface WithNameClass {  new (name: string): Animal1}function createClass(classname: WithNameClass, name: string) {  return new classname(name)}let instance1 = createClass(Animal1, 'monkey')// let instance2 = createClass(Animal2, 'monkey') // 没有name属性则报错

其它任意属性

interface Person2 {  readonly id: number  [propName: string]: any //任意属性}

泛型

泛型是指定义函数、接口或类的时候,不预先指定具体的类型,而在应用的时候再指定类型的一种个性;通常用 T 示意,但不是必须应用改字母,只是惯例,通常还有其余罕用字母:

  • T(Type):示意一个 TypeScript 类型
  • K(Key):示意对象中的键类型
  • V(Value):示意对象中的值类型
  • E(Element):示意元素类型

泛型类

class GenericNumber<T> {  name: T  add: (x: T, y: T) => T}let generic = new GenericNumber<number>()generic.name = 123

泛型数组

  • 写法一
function func<T>(params: T[]) {  return params}func<string>(['1', '2'])func<number>([1, 2])
  • 写法二
function func1<T>(params: Array<T>) {  return params}func1<string>(['1', '2'])func1<number>([1, 2])

泛型接口

能够用来束缚函数

interface Cart<T> {  list: T[]}let cart: Cart<number> = { list: [1, 2, 3] }

泛型别名

type Cart2<T> = { list: T[] } | T[]let c1: Cart2<number> = { list: [1, 2, 3] }let c2: Cart2<number> = [1, 2, 3]

泛型接口 VS 泛型别名

  • 接口创立了一个新的名字,它能够在其余任意中央被调用。而类型别名并不创立新的名字
  • 类型别名不能被 extends 和 implements,这时咱们应该尽量应用接口代替类型别名
  • 当咱们须要应用联结类型或者元组类型的时候,类型别名会更适合

多个泛型

// 不借助两头变量替换两个变量的值function swap<T, P>(tuple: [T, P]): [P, T] {  return [tuple[1], tuple[0]]}let ret = swap([1, 'a'])ret[0].toLowerCase()ret[1].toFixed(2)

默认泛型

function createArray<T = number>(length: number, value: T): T[] {  let arr: T[] = []  for (let i = 0; i < length; i++) {    arr[i] = value  }  return arr}let arr = createArray(3, 9)

泛型束缚(继承)

interface WithLength {  length: number}// extends 来继承function logger<T extends WithLength>(val: T) {  console.log(val.length)}logger('hello')logger([1, 2, 3])// logger(true) // error 没有length属性

泛型工具类型

为了不便开发者 TypeScript 内置了一些罕用的工具类型,比方 Partial、Required、Readonly、Pick 等。它们都是应用 keyof 实现。

keyof 操作符能够用来一个对象中的所有 key 值。

interface Person1 {  name: string  age: number  sex?: string}type PersonKey = keyof Person1 // 限度 key 值的取值function getValueByKey(p: Person1, key: PersonKey) {  return p[key]}

内置的工具类型

// type Partial<T> = {[P in keyof T]?: T[P]}type PersonSearch = Partial<Person> // 全副变可选// type Required<T> = { [P in keyof T]-?: T[P] }type PersonRequired = Required<Person> // 全副变必选// type ReadOnly<T> = { readonly [P in keyof T]: T[P] }type PersonReadOnly = Readonly<Person> // 全副变只读// type Pick<T, K extends keyof T>  = {[P in K]: T[P]}type PersonSub = Pick<Person, 'name'> // 通过从Type中抉择属性Keys的汇合来构造类型。

类数组

let root = document.getElementById('root')let children: HTMLCollection = root!.children // ! 代表非空断言操作符let childNodes: NodeListOf<ChildNode> = root!.childNodes

类型爱护

更明确的判断某个分支作用域中的类型,次要尝试检测属性、办法或原型,以确定如何解决值。

typeof

function double(input: string | number | boolean): number {  // 根本数据类型的类型爱护  if (typeof input === 'string') {    return input.length  } else if (typeof input === 'number') {    return input  } else {    return 0  }}

instanceof

class Monkey {  climb: string}class Person {  sports: string}function getAnimalName(animal: Monkey | Person) {  if (animal instanceof Monkey) {    console.log(animal.climb)  } else {    console.log(animal.sports)  }}

in

class Student {  name: string  play: string[]}class Teacher {  name: string  teach: string}type SchoolRole = Student | Teacherfunction getRoleInformation(role: SchoolRole) {  if ('play' in role) {    console.log(role.play)  }  if ('teach' in role) {    console.log(role.teach)  }}

自定义类型爱护

比方有时两个类型有不同的取值,也没有其余能够辨别的属性

interface Bird {  leg: number}interface Dog {  leg: number}function isBird(x: Bird | Dog): x is Bird {  return x.leg === 2}function getAnimal(x: Bird | Dog): string {  if (isBird(x)) {    return 'bird'  } else {    return 'dog'  }}

上述残缺代码示例:typescript-tutorial