typeScript学习笔记

85 阅读7分钟

为什么需要TS呢?

  1. 使用ts的时候在写代码的时候机会抛出错误,并会抛出相关错误。
  2. 编写代码会提示相关参数的提示。
  3. 对代码的声明说明,会更好理解代码。

viod和undefined、null的区别?

viod代表变量为undefined或者null。

viod不可以将值赋值给其他变量,而undefined和null可以。

any和unknown有什么区别?

unknown类型比any类型更安全。unknown类型引用他的属性和方法时,ts会报错,同时只能赋值unknown和any对象,复制给其他对象会报错。

什么是接口interface?

interface定义一个对象里面所有的属性及属性类型。如果两个interface名称一样会合并。

可选式操作符?:则表明此属性可出现可不出现。

任意属性[propName:String]:any允许添加新的任意属性。需要注意的是一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。如下定义就会报错:

interface Person {
  name:string,
  age?:number,//会报错类型“number”不能赋给“string”索引类型的“string”
  [propName: string]: string
}

联合类型|可扩展类型。

交叉类型 & 满足两个或多个接口的交叉类型。

类型断言as:

let fn = function(num:number | string):void {
  console.log((num as string).length)//只有断言正确才会输出其length
  //或者
  console.log((<String>num).length)
}
fn('123')

readonly只读类型,不能赋值

interface Person {
  readonly name:string,
  age?:number,
  [propName: string]: any
}

interface定义函数方式

interface Fn {
  cb():number//number代表返回值的类型
}

let f:Fn = {
  cb: ():number => {
    return 123
  }
}

组合式interface

interface A{
  name:string
}
interface B extends A {
  age:number
}
let p:B = {
  name:'张三',
  age:12
}

数组声明

数组声明的时候如果指定一个类型,则有其他类型会报错。

var arr:number[] = [1,2,3]//数字类型的数组
var arr2:string[] = ["1","2","3"]//数字类型的数组
var arr3:any[] = [1,'2',true]//任意类型的数组
//或者
let arr:Array<number> = [1,2,3,4]

多维数组

let arr:number[][][] = [[[]]]

let arr:Array<Array<number | string>> = [[1,2,3,'124']] 

类数组

function foo(...arg:any):void {
  let arr:IArguments = arguments
}

foo(1,2,3)

//IArguments相当于
interface IArguments {
  [index: number]: any;
  length: number;
  callee:Function;
}

接口描述数组

// 接口描述数组
interface ArrNumber {
  [index:number] : string
}

let arr: ArrNumber = ['1','2','3']

函数重载

如下方式:

function fn(p1:number):void
function fn(p1:string, p2:number):void
function fn(p1:any,p2?:any):void {
  console.log(p1,p2);
}

fn(1)
fn('123',233)

在执行函数的上方可以定义多个规则,会根据调用的方式选择不同的规则。执行函数的定义要包含所有的定义规则。

类的修饰符

public 内部外部都能访问,默认是public

private 是私有属性,只能内部访问

protected是私有属性,内部和子类能访问

静态属性 static,可直接访问,静态属性里面不能this访问除静态属性其他的值,同时内部属性除去静态属性this能访问它,其他属性要通过类名来访问。

类的参数要在constructor上面定义。

class Person {
  public name:string
  public age:number
  public sub:boolean//声明了必须要用
  private dd: string = '2'
  static aa:number = 200
  constructor(name:string,age:number,sub:boolean,dd:string) {
    this.name = name
    this.age = age
    this.sub = sub 
    this.dd = dd
    Person.dev()//this访问不了
  }

  static dev () {
    console.log(this.aa);
    
  }

  static run () {
    this.dev()
  }
}
Person.run()

用interface定义一个类

用interface可以定义一个类,使用implements和类关联起来。

interface Person {
  run(type: boolean): boolean
}

class Person implements Person {
  run(type: boolean): boolean {
    return type
  }
}

当时用多个interface定义一个类时,用,连接。

interface Person {
  run(type: boolean): boolean
}
interface H {
  set():void
}

class Person implements Person, H {
  run(type: boolean): boolean {
    return type
  }
  set(): void {
      
  }
}

继承父类的写法:

interface Person {
  run(type: boolean): boolean
}
interface H {
  set():void
}

class A {
  params: string
  constructor(params) {
    this.params = params
  }
}

class Person extends A implements Person, H {
  run(type: boolean): boolean {
    return type
  }
  set(): void {
      
  }
}

抽象类

使用场景?

抽象类定义abstract,抽象类无法实例化。使用abstract定义的函数,在派生类中要实例化函数,不然会报错(相当于类型定义)

abstract class A {
  name:string
  constructor(name:string) {
    this.name = name
  }

  abstract getName(): string
}

class B extends A {
  constructor() {
    super('DO')
  }
  getName(): string {
    return this.name
  }
}

let b = new B()
console.log(b.getName());

如果抽象类中函数定义没有使用abstract则可以直接使用。

abstract class A {
  name:string
  constructor(name:string) {
    this.name = name
  }

  abstract getName(): string

  setName(name:string):void {
    this.name = name
  }
}

class B extends A {
  constructor() {
    super('DO')
  }
  getName(): string {
    return this.name
  }
}

let b = new B()
console.log(b.getName());
b.setName('kuswin')
console.log(b.getName());

元组类型

元组(Tuple)是固定数量的不同类型的元素组合,是数组的变种。

let arr:[string,number] = ['do',1232]

枚举类型(enum)

enum Color {
  rad,//0
  green,//1
  blue//2   默认递增,可以自定义
}
enum Color {
  red = 1,//1
  green,//2
  blue//3
}

相当于:

return type === 'red' ? 0 : type === 'green' ? 1 : 2

异构枚举

混合字符串和数字成员:

enum Types {
  No = 'No',
  Yes = 1
}

接口枚举:

enum Types {
  No = 'No',
  Yes = 1
}

interface A { 
  red: Types.Yes
}

let obj: A = {
  red: Types.Yes
}

使用const定义enum,编译出的enum为常量,不适用const 则编译为对象。

枚举类型也可通过value访问key值。(反向一射)

enum Types {
  code
}

let code:number = Types.code//0
let key = Types[code]//code
console.log(code,key);

但是字符串类型反向会报错。

类型推论|类型别名type

type可以用来声明一个值的类型,可以联合。

type A = string | number
let a:A= 222
type A = 'off' | 'on' 
let a:A= 'off'//只能'off'/'on'

never不存在状态

never来表示不应该存在的状态:

表示这是不可能的事,程序出了问题。

使用场景:

function error(message:string):never {
  throw new Error(message)
}
function loop():never {
  while(true) { 
  }
}

也可作为兜底类型。

Symbol类型

let s:symbol = Symbol('哈哈哈哈')

symbol类型内存地址指针位置不同所以是唯一值。

let s:symbol = Symbol('123')
let r:symbol = Symbol('123')
console.log(s === r);//false

Symbol作为对象key值时,不可遍历。

迭代器|生成器

迭代器

type mapKeys = string | number
let arr:Array<number> = [4,5,5]

let set:Set<number> = new Set([1,2,3,4])
let map:Map<mapKeys,mapKeys> = new Map()
map.set('23','kkkk')
map.set(445,'kkkk')

function fn(arg:any) {
  let it:Iterator<any> = arg[Symbol.iterator]()//不支持对象
  let next:any = {done:false}
  while(!next.done) {
    next = it.next()
    if(!next.done) {
      console.log(next);
    }
  }
}

fn(map)

生成器

for..of

泛型

是为了解决同一个函数,但是类型不一样。

语法为函数名字后面加一个<参数名>,参数名可以随意写。

当使用这个函数的时候把参数的类型传进去就可以(动态类型)

function fn<T> (a:T,b:T):Array<T> {
  return [a,b]
}
fn<number>(1,2)

多个类型:

function fn<T,U>(a:T,b:U):Array<T | U> {
  return [a,b]
}
fn<number,string>(1,'2')

泛型约束 :

interface Len {
  length:number
}
//当时用.length的时候,没有定义参数这个属性,会报错,可以约束一下参数的这个类型
function fn<T extends Len>(arg:T) {
  return arg.length
}
fn<Array<number>>([1])

使用keyof约束对象:

首先定义T类型,并使用extends关键字继承object类型的子类型,然后使用keyof操作符获取T的所有键,返回的类型是联合类型,最后使用extends关键字约束K类型必须为keyofT联合类型的子类型。

function prop<T,K extends keyof T>(obj:T,key:K) {
  return obj[key]
}
let obj = {
  a:1,
  b:2,
  c:3
}
prop(obj,'a')
prop(obj,'d')//会提示报错 obj内没有定义key值为‘d’的属性,key的类型为 'a' | 'b' | 'c'

泛型类

class Sub<T> {
  attr: T[] = [];
  add(a:T):T[] {
    return [a]
  }
}
let sub = new Sub<number>()
sub.attr = [122]
sub.add(333)

namespace命名空间

namespace命名空间可防止全局变量污染,可以嵌套,可以抽离。

三斜线指令///

导入其他文件,导入后可直接使用导入文件的变量,编译后会编译成一个文件。

/// <reference path="./index.ts" />

声明文件引入

/// <reference types="node" />

表明这个文件使用了@types/node/index.d.ts里面声明的名字,这个包需要在编译阶段与声明文件一起被包含起来。同时你需要写一个d.ts文件时才能使用这个指令。

注意事项:

如果你在配置文件 配置了noResolve 或者自身调用自身文件会报错