ts 基础

308 阅读6分钟

.ts 和 .tsx 后缀

1、.tsx = ts + jsx 语法;

2、.ts = ts ;

3、webpack 中可以配置,.ts = ts + jsx ;

type(类型) 和 class(类)

1、type: 数据类型 string/number/boolean/symbol/bigint null/undefined object

2、typeof 返回 type

typeof 函数,返回 function;
typeof null,返回 object;

3、class

JS 面向对象的编程:基于 class 关键字的面向对象编程、基于原型的面向对象编程。

可见性修饰符:控制 class 的方法或属性对于 class 外的代码是否可见。包括 public/protected/private 1、public:表示公有的,公有成员可以被任何地方访问,默认值,可省略。

2、protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。

class Animal {
  protected move(){
    console.log('移动')
  }
  run(){
    this.move()
    console.log('跑')
  }
}
const a = new Animal()
a.move()  //报错

class Dog extends Animal{
  bark(){
    this.move()
    console.log('叫')
  }
}
const d = new Dog()
d.move()  //报错

3、private:表示私有的,只在当前类中可见,对实例对象和子类不可见。

class Animal {
  private __run__(){
    console.log('Animal 内部辅助函数')
  }
  protected move(){
    this.__run__()
    console.log('移动')
  }
  run(){
    this.__run__()
    this.move()
    console.log('跑')
  }
}
const a = new Animal()
a.move()  //报错
a.__run()__  //报错

class Dog extends Animal{
  bark(){
    this.__run__()  //报错
    this.move()
    console.log('叫')
  }
}
const d = new Dog()
d.move()  //报错
d.__run__()  //报错

ts 的好处

1、报错

function add(a:number, b:number){return a + b}
add('1'  //写代码时就会检查是否有错误,不需要等到编译执行

2、提示,ts 会自动进行类型匹配

image.png

语法

const a:undefined = undefined
const b:null = null
const c:string = 'hi'
const d:boolean = false
const e:number = 3
const f:symbol = Symbol('hi')
const g:bigint = 123n
//const obj:object = {}  小写 object 是类型
//const obj:Object = {}  大写 Object 是类,因为Object 有更加详细的类,一般不这样写,用下面的写法

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

//函数

//1. 类型写在函数体,输入类型、输出类型
const add1 = (a:number,b:number):number => a + b

//2. :type
const add2:(a:number,b:number) =>number = (a,b) => a + b

//3. type 缩写
type Add = (a:number,b:number) => number
const add3: Add = (a,b) => a + b

//4. 有属性,只能用 interface
interface AddWithProps {
    (a:number,b:number):number
    xxx:string
}
const add4:AddWithProps = (a,b) => a + b
add4.xxx = '123'

ts 独有的

any:任意类型;

unknown:一般是从网络上获取到的数据,现在不知道是什么类型,但是使用时需要明确类型,可以用断言;

void:没有类型,一般用在函数里面,表示没有返回值;

never:可以用集合理解,当交集为空时,类型就是 never,代码中出现 never 时,一般就是出问题了;

enum:枚举,可以为一组数值赋予名字,默认从 0 开始,枚举的好处是可以由枚举的值得到它的名字;

元祖:表示一个已知元素数量和类型的数组,每一个的类型都要写清楚;

readonly:只能修饰属性,不能修饰方法,修饰的属性需要手动提供明确的类型;

let a:any = 'hi'
let b:unknown = JSON.parse('{"name":"jason"}')
type C = {name:string}
console.log((b as C).name)  //断言,使用时声明类型

let fn:()=>void = function(){
    console.log(1)
}

type Dir = 1 | 2 | 3 | 4 | undefined
let dir:Dir
switch(dir){
    case 1:
        break;
    case 2:
        break;
    case 3:
        break;
    case 4:
        break;
    case undefined:
        break;
    default:
        console.log(dir)  //dir 的类型为 never
        break;
}

type X = number & string  //交集为空,X 的类型 never
type Y = (1|2|3) & (2|3|4)  //Y 类型 (2|3)

let p:[number,string,boolean] = [1,'33',true] 

enum Dir2 {东,南,西,北}
let d:Dir2 = Dir2.东  //0
let e:Dir2 = Dir2.北  //3
console.log(d)  //0

//枚举可以用以下写法代替
type Dir2 = '东' | '南' | '西' | '北'
let d:Dir2 = Dir2.东
let d:Dir2 = Dir2.北

联合类型 |

1、如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员;

2、类型区分:如果是简单类型,可以用 typeof 做类型区分;复杂类型的话,需要被联合起来的类型有一个相同的 key ,然后用 if、switch 判断做分支选择,tagged type

type A = {
    name:'a';
    age:number;
}

type B = {
    name:'b';
    gender:string;
}

const fn1 = (n:A | B)=>{
    console.log(n.name)    //ok
    console.log(n.gender)  //报错
}

const fn2 = (n: number | B)=>{
  if(typeof n === 'number'){
      n.toFixed()
  }else{
      n.name
  }
}

const fn3 = (n:A | B)=>{
    if(n.name === 'a'){
        n.age
    }else{
        n.gender
    }
}

交叉类型 &

不能用于简单类型之间,一般用于 object 之间;

& 表示同时拥有这些属性,当属性的 key 相同时,类型也要一样,如下:两个 age 的类型一样,都是 number;

type A = number & string   //A 为 never
type B = {name:string,age:number} & {age:number,gender:string}

let a:B = {
    name:'jason',
    age:18,
    gender:'男'
}

div 的类型

const d:HTMLDivElement= document!.getElementById('xxx') as HTMLDivElement
const value = (document!.getElementById('xx')! as HTMLInputElement).value
const f = (x:number) => x+1
f(parseInt(value))

泛型

将类型用变量表示,可以将泛型理解为函数调用,尖括号用圆括号代替,主要是支持不同的类型。

type F<T> = T | [T]
type FNumber = F<number>
type FString = F<string>

type Add<T> = (a:T,b:T) => T
const addN:Add<number> = (a,b) => a + b
const addS:Add<string> = (a,b) => a + b
  • 泛型约束

因为泛型的类型太多,参数的属性可能只有某些类型上才有,比如数组的 length 属性,其它的类型可能没有这个属性,此时,需要给泛型添加约束:

interface Length{
  length:number
}

function id<T extends Length>(value:T):T{
  console.log(value.length)
  return value
}

//或者
function id<T>(value:T[]):T[]{
  console.log(value.length)
  return value
}
  • 多个泛型变量
function getProp<T,K extends keyof T>(obj:T,key:K){
  return obj[key]
}
let person = {name:'jack',age:18}
getProp(person,'name')

keyof 关键字接受一个对象类型,生成其键名的联合类型:name | age

  • 泛型工具 1、索引签名类型:

无法确定对象中有哪些属性时使用; key 是占位符,可用任意词。

interfac AnyObject{
  [key:string]:number
}

2、Record<keys,T>:

构造一个对象类型,属性键为 keys ,属性类型为 T

type RecordObj = Record<'a' | 'b' | 'c',string[]>
let obj:RecordObj = {
  a:['1'],
  b:['2'],
  c:['3']
}

3、映射类型:只能在类型别名中使用,不能在接口中使用。

type PropKeys = 'x' | 'y' | 'z'
type Type1 = {x:number; y:number; z:number}

//使用映射类型
type PropKeys = 'x' | 'y' | 'z'
type Type2 = {[Key in PropKeys]:number}

-------------------
type Props = {a:number; b:string; c:boolean}
type Type3 = {[Key in keyof Props]:number}

Key in PropKeys表示 Key 可以是 PropKeys 联合类型中的任意一个,类似于 for{let key in obj}

  • Partial<Type> 的实现
type Partial<Type> = {
  [P in keyof Type]?: Type[p]
}

type Props = {a:number; b:string; c:boolean}
type PartialProps = Partial<Props>
  • 索引查询类型:用来查询属性的类型 Type[p] 表示获取 Type 中每个键对应的类型。
type Props = {a:number; b:string; c:boolean}
type A = Props['a' | 'b']   // number | string
type B = Props[keyof Props]  // number | string | boolean

递归类型

type A = Array<number | A >
const b:A = [1,[2],[2,[3]],[[[4]]],[[[[5]]]]]
const a:Array<number> = [1,2,3]
a.push()

type Tree = {
    id:string | number;
    children:Tree[]
}

var tree = {
    id:1,
    children:[
        {id:2,children:[]}
    ]
}

类型声明文件:.d.ts文件

  • 为已有 js 文件提供类型声明: 1、在导入 .js 文件时,TS 会自动加载与 .js 同名的 .d.ts 文件,以提供类型声明。

2、declare:用于类型声明,为其它地方(比如:.js 文件)已存在的变量声明类型,而不是创建一个新的变量。

3、对于 type/interface 等明确是 TS 类型的(只能在 TS 中使用的),可以省略 declare 关键字。

4、对于 let/function 等具有双重含义(在 TS、JS 中都能用),应该使用 declare 关键字,明确指定此处用于类型声明。

image.png

ts.config.json 常见配置项

image.png

image.png

重载

让一个函数满足多种参数条件,常见方法使用:typeof、length

function add(a:number,b:number):number ;
function add(a:string,b:string):string;
function add(a:any,b:any):any {
    if(typeof a === 'number' && typeof b === 'number'){
        return a + b
    }else{
        return a + '' + b
    }
}
add(1,2)
add('hello','world')


type Options = {header?:any}
function get(url:string,options:Options):void
function get(options:Options & {url:string}):void
function get(url:string | (Options & {url:string}),options?:Options):void{
    if(arguments.length === 1){
        const myOptions = url as {url:string} & Options
        myOptions.url
    }else{
        console.log(url as string)
    }
}

泛型封装网络请求

type user = {
    id:string | number;
    name:string;
    age:number;
}
type Response<T> = {
    data:T
}

type Y = Partial<user>  //给所有的 key 加问号

type O = Omit<user,'id'>  //省略 id

type T =Partial<Omit<user,'id'>>
type CreateResource = (path:string) =>{
    create:(attrs:Omit<Partial<user>,'id'>) =>Promise<Response<user>>
    delete:(id:user['id']) => Promise<Response<never>>
    update:(id:user['id'],attrs:Omit<Partial<user>,'id'>) => Promise<Response<user>>
    get:(id:user['id']) => Promise<Response<user>>
    getPage:(page:number) => Promise<Response<user[]>>
}

const createResource:CreateResource = (path) => {
    return {
        create(attrs){}
        delete(){}
        update(){}
        get(){}
        getPage(){}
    }
}

var userResource = createResource('/api/user')