typescript学习

前言

JavaScript是一门弱类型语言,无需编译就能运行,没有类型检查,无法提前预知错误,并且没有类型约束增加了沟通成本。我们知道,在开发时,越早发现错误越好。TypeScript是拥有类型的JavaScript超集,相当于JavaScript的加强版,它可以编译成完整的JavaScript代码。

JavaScript所拥有的特性,TypeScript全部都是支持的,并且它紧随ECMASript的标准,所以ES6、ES7、ES8等新语法标准,它都是支持的;TypeScript在语言层面上,不仅仅增加了类型约束,而且包括一些语法的扩展,比如枚举类型,元组类型等。


变量的声明

// var/let/const 标识符: 数据类型 = 值;
let message: string = '张三';
//当我们给 message 赋值其他类型的值,那么就会报错
message: 123   //不能将类型“number”分配给类型“string”。ts(2322)

变量的类型推导

// 有时候我们开发时不需要每次声明变量都要事先定义好数据类型,而是可以通过ts的类型推导来推断出变量类型
let age = 123
age = '123'  // 不能将类型“string”分配给类型“number”。ts(2322)

数据类型

/* JavaScript */
let age: number = 22			//number
let name: string = 'zhangsan'	//string
let isReload: boolean = true	//boolean
let arr: number[] = [1, 2, 3]	//Array
let arr1: Array<number> = [4, 5, 6]
let obj: object = {				//object
  name: 'zhangsan',
  age: 12,
  gender: 'male'
}
let s1: symbol = Symbol('zhangsan')		//symbol
let s2: symbol = Symbol('zhangsan')
let person = {
  [s1]: 'male',
  [s2]: 'female'
}
let n: null = null				// null
let u: undefined = undefined       //undefined

/* TypeScript */
let a: any = 'why'      // any 任意类型,宽松的(无约束)
a = 123  // 可以
let result: unknown;    // unknown 类型不确定的变量
function sum(num1: number, num2: number): void {	// void 通常用于指定函数没有返回值
  console.log(num1 + num2)
}
function loopErr(): never {				//never 表示永远不会发生值的类型,比如函数的死循环或者抛出异常(中断),无法  返回值
  throw new Error()
}
let student: [string, number, string] = ['zhangsan', 12, 'male'];        // tuple元组类型,即多种数据类型组合的数组

函数的参数类型

函数是JavaScript非常重要的组成部分,TypeScript允许我们指定函数的参数和返回值的类型。

function delete(id: string): object{       // 这里指定传入的参数必须为 string 返回值是一个对象
  let res = {}
  return res
}
function search(searchInfo: {name: string, age: number}): object{       // 这里指定传入的参数必须为对象且属性,属性值需要对应符合
  let res = {}
  return res
}
search({name: 'zhangsan', age: 2})  

可选类型

  • 对象类型也可以指定哪些属性是可选的,可以在属性的后面添加一个?:

  • 注意可选类型后面不能有必选类型,即可选类型一般放最后,类似于…rest(剩余参数)

    function search(id: string, name?: string) {
    console.log(id)
    if(name) console.log(name)
    }
    search(‘123’,‘zhangsan’) // 123,zhangsan
    search(‘123’) // 123

联合类型

TypeScript的类型系统允许我们使用多种运算符,从现有类型中构建新类型

function printId(id: number | string) {
  if(typeof id === 'number') return id		
  else return id.length
}
console.log(printId(10))    //10
console.log(printId('abc'))   // 3

到这里其实可以看出,可选类型是某一类型与 undefined 的联合类型,如:

function printId(id?: number){
  console.log(id)
}
printId()
printId(123)  // 123

类型别名

在前面,我们通过在类型注解中编写 对象类型 和 联合类型,但是当我们想要多次在其他地方使用时,就要编写多次。 比如我们可以给对象类型起一个别名:

type Point = {x: number, y: number}

function printPoint(point:Point) {
  console.log(Point.x, Point.y)
}
printPoint({x: 1, y: 2})

类型断言as

有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言

// 不使用断言
function printLength(n: string | number) {
  console.log(n.length);    //类型“string | number”上不存在属性“length”。类型“number”上不存在属性“length”。ts(2339)
}

// 使用断言
function printLength(n: string | number) {
  console.log((n as string).length);
}

函数的默认参数

从ES6开始,,JavaScript是支持默认参数的,TypeScript也是支持默认参数的:

function getInfo(id: string, status: number = 2) {
  console.log(id, status)
}
foo('213')   // 213, 2
// 这个时候y的类型其实是 undefined 和 number 类型的联合。

函数的剩余参数

从ES6开始,JavaScript也支持剩余参数,剩余参数语法允许我们将一个不定数量的参数放到一个数组中。

function sum(...nums: number[]) {
  let total = 0
  for(let num of nums) {
    total += sum
  }
  console.log(total)
}
sum(1, 2, 3)   // 6

函数的重载

function sum(num1: number, num2: number): number;    
function sum(num1: string, num2: string): string;   //重载外部不可见
function sum(num1: any, num2: any): any {
	return num1 + num2
}
console.log(sum(1, 2))    // 3
console.log(sum('zhang', 'san'))  // zhangsan

接口的声明

interface People = {
  name: string
  age: number
}
  • 接口中的可选属性

    interface Person {
    name: string
    age: number
    gender?: string
    }
    const person: Person = {
    name: ‘zhangsan’,
    age: 15,
    gender: ‘female’
    }
    console.log(person.name) // zhangsan
    console.log(person?.gender) // female

  • 接口中的只读属性

    interface Person {
    readonly name: string
    readonly age: number
    gender?: string
    }
    const person: Person = {
    name: ‘zhangsan’,
    age: 12,
    gender: ‘male’
    }
    person.age = 14 //无法分配到 “age” ,因为它是只读属性。ts(2540)

可以发现,readonly 和 const 有像似之处

  • 同:用于约束数据的修改属性
  • 异:const用于定义常量,而 readonly用于修饰属性

索引类型

interface Game {
  [index: string]: string
}
const game: Game = {
  'first': '天天酷跑',
  'second': '五子棋',
  'third': '象棋'
}
console.log(game.first)  //天天酷跑

接口继承

接口和类一样是可以进行继承的,也是使用extends关键字:

interface Person {
  name: string,
  eating: () => void
}

interface Animal {
  running: () => void
}

interface Student extends Person, Animal {
  gender: string
}

const student: Student = {
  gender: 'male',
  name: 'zhangsan',
  eating: function() {},
  running: function() {}
}

接口的实现

接口定义后,也是可以被类实现的:

  • 如果被一个类实现,那么在之后需要传入接口的地方,都可以将这个类传入

  • 这就是面向接口开发

    interface ISwim {
    swimming: () => void
    }

    interface IRun {
    running: () => void
    }

    class Person implements ISwim, IRun {
    swimming() {
    console.log(‘swimming’)
    }

    running() {
      console.log('running')
    }
    

    }

    function swim(swimmer: ISwim) {
    swimmer.swimming()
    }

    const p = new Person()
    swim§ //swimming

交叉类型

交叉类型用 & 符号表示,表示需要满足多个类型的条件

type x = number & string           // 没有同时满足条件的值,所以当交叉的类型是基本数据类型时相当于 never 类型

// 常用于对象类型进行交叉
interface Animal {
  eating: () => void
}

interfact Dog {
  running: () => void
}

type NewType = Dog & Animal
const dog: NewType = {
  eating: function() {}
  running: function() {}
}

枚举类型

枚举类型是TypeScript特性之一:

  • 枚举其实就是将一组可能出现的值,一个个列举出来,定义在一个类型中,这个类型就是枚举类型

  • 枚举允许开发者定义一组命名常量,常量可以是数字、字符串类型

    enum Direction {
    top,
    right,
    bottom,
    left
    }

枚举类型的值

枚举类型默认是有值的,比如上面的枚举,默认值是这样的

enum Direction {
  top = 0,
  right = 1,
  bottom = 2,
  left = 3
}
// 当然也可以赋值给其他类型
enum Direction {
  top = 'bottom',
  right = 'left',
  bottom = ,
  left = 
}

泛型

泛型是方便增强函数的复用性(抽象)

// 当我们需要封装一个函数, 传入一个参数,并且返回这个参数时。
function foo (arg: number): number {      //实现了number类型,但不符合string、boolean等类型
  return arg            
}
// 使用 any
function foo1(arg: any): any {
  return arg                 // 虽然可以实现需求,但这时候已经丢失了类型信息
}
// 使用泛型可以完美解决问题
function foo2<T>(arg: T): T {
  return arg                
}
console.log(foo2(1))
console.log(foo2(true))
console.log(foo2('sad'))

当需要传入多个类型时

function foo<T, E>(name: T, age: E) {
  console.log(name, age)
}

泛型接口

在定义接口的时候我们也可以使用泛型

interface Animal<T> {
  name: T,
  friends: T[],
  sayHello: (value: T) => void
}

const cat: Animal<string> = {
  name: '蓝猫'
  friends: ['虹猫','咖啡猫']
  sayHello: function(value: string) {
    console.log(value)
  }
}

泛型类

class Animal<T> {
  name: T
  shape: T
  
  constructor(name: T, shape: T) {
    this.name = name
    this. shape = shape
  }
}

const dog = new Animal('二哈','middle')
const cat = new Animal<string>('蓝猫', 'small')
const pig: Animal<string> = new Animal('天蓬', 'big')

泛型约束

有时候我们希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中

  • 比如string和array都是有length的,或者某些对象也是会有length属性的

    interface ILength {
    length: number
    }

    function getLength(args: T) {
    return args.length
    }
    console.log(getLength(‘abc’)) //3
    console.log(getLength([1,2,3])) //3
    console.log(getLength({name: ‘zhangsan’, age: 12, length: 180})) //180

命名空间namespace

命名空间在TypeScript早期时,称之为内部模块,主要目的是将一个模块内部再进行作用域的划分,防止一些命名冲突的问题。

export namespace Time {
  export function format(time: string) {
    return '2022-01-27'
  }
}

export namespace Prize {
  export function format(price: number) {
    return '九磅十五便士'
  }
}
上一篇:Random


下一篇:中国熔断器市场需求分析及未来发展趋势预测报告2022-2027年