TypeScript相关

TS基础语法

1
2
3
4
5
6
7
8
9
const a:string = 'hi'
const b:number = 111
const c:boolean = true
const d:null = null
const e:undefined = undefined
const f:symbol = Symbol('hi')
const g:bigint = 123n
const obj:Object = {}
const arr:Array<string | number | null> = ['1','2', 3, null]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 函数的类型声明
// 1. 类型写在函数体里
const add1 = (a:number, b:number) => a+b
// 2. 写在 :后面
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 add2:AddWithProps = (a,b) => a+b
add2.xxx = 'Hello World'

联合类型

  • 联合类型:由两种或多种其他类型组成的类型,表示可能是这些类型中的任何一种的值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    type A = {
    name: 'a';
    age: number
    }
    type B = {
    name: 'b',
    gender: string
    }
    // 联合A与B两种类型
    // 使用要点:A、B两个类型中必须至少有一个相同的key,然后才能根据这个key的值做区分
    const f = (n: A|B) => {
    if(n.name === 'a'){ // 类型A与类型B中有相同的key,即name
    n.age
    } else {
    n.gender
    }
    }

TS vs. JS

  • 语法层面:TypeScript = JavaScript + Type(TS是JS的超集)
  • 执行环境:浏览器、nodejs可以直接执行JS,但不能执行TS(Deno可以执行TS)
  • 编译层面:TS有编译阶段,JS没有编译阶段(只有转译和ESlint阶段)
  • 编写层面:TS更难写一些,但类型更安全
  • 文档层面:TS代码写出来就是文档,IDE可以完美提示。JS提示主要靠TS

将TS转译为JS的方法

  1. webpack + babel
  2. Vite 2
  3. tsc 即 typescript compiler

使用TS的好处

  1. 检查bug:增加类型检查,可以在写代码(编译时)就报错,不需要等到代码运行时
  2. 智能提示:要求你做好类型判断(收窄类型),写代码更安全
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import React from 'react'
    import ReactDOM from 'react-dom'
    const root = document.getElementById('root')
    // 这里root可能的类型为 HTMLElement || null

    if(root !== null) { // 判断 root不为null 才执行
    ReactDOM.render(<div>hi</div>, root)
    } else {
    report('root为空')
    }

any vs. unknown

  • 两者都是顶级类型(top type),任何类型的值都可以赋值给顶级类型变量

    1
    2
    let foo:any = 123;  // 不报错
    let bar:unknown = 123; // 不报错
  • 但 unknown 比 any 的类型检查更严格,any什么检查都不做,unknown要求在使用前先明确类型(类型收窄)

    1
    2
    const value:unknown = "Hello World";
    const someString:string = value; //报错:Type 'unknown' is not assignable to type 'string'
1
2
const value: unknown = "Hello World";
const someString:string = value as string; // 不报错
  • 若改为any,基本在哪都不报错。所以优先用unknown,类型更安全

never

  • never是底类型,表示不应该出现的类型(什么都不是,出现never表示代码有问题)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    interface A {
    type: 'a'
    }
    interface B {
    type: 'b'
    }
    type All = A | B

    function handleValue(val: All) {
    switch (val.type) {
    case 'a':
    // 这里 val 被收窄为 A
    break

    case 'b':
    // val 在这里是 B
    break

    default:
    // val 在这里是 never
    const exhaustiveCheck: never = val
    break
    }
    }

type 和 interface 的区别?

  1. 组合方式:interface 使用 extends 来实现继承,type 使用 & 来实现联合类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // interface的继承方式
    interface B {
    b: string
    }
    interface A extends B {
    a: string
    }

    const a:A = {
    a: 'hi',
    b: 'hi'
    }
    // type的继承方式
    type BB = {
    bb: string
    }
    type AA = {
    aa: string
    } & BB

    const aa:AA = {
    aa: 'hi',
    bb: 'hi'
    }
  2. 扩展方式:interface 可以重复声明用来扩展,type 一个类型只能声明一次

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    interface A {
    a:string
    }
    // interface可以重复声明,且两次声明会自动合并
    interface A {
    b:string
    }
    const a:A {
    a: 'hi',
    b: 'hi'
    }
1
2
3
4
5
6
7
type A = {
a: string
}
// 再次声明A会报错,用type声明变量后不可变
type A = {
b: string
}
  1. 范围不同:type 适用于基本类型,interface 一般不行
  2. 命名方式:interface 会创建新的类型名,type 只是创建类型别名,并没有新创建类型
    1
    2
    3
    type X = number  // 给number取了一个别名为 X
    const x:X = 1
    type Y = typeof x // Y === number

TS工具类型的作用和实现

  1. Partial(部分类型)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 创建一个用户,但用户id需要上传服务器后才能拿到
    interface User {
    id: string;
    name: string;
    }

    const user: Partial<User> = {
    name: 'joyce' // 创建用户时可以先不传id
    }
  2. Required(必填类型)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    interface User {
    id: string;
    name: string;
    }

    const user: Required<User> = { // id和name都必填
    id: '111',
    name: 'joyce'
    }
  3. Readonly(只读类型)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    interface User {
    id: string;
    name: string;
    }

    const user: Readonly<User> = {
    id: '111',
    name: 'joyce'
    }

    user.id='222' // 报错,user是只读的,不可更改
  4. Exclude(排除类型)&& Extract(提取类型) => 后接基本类型

    1
    2
    3
    4
    type Dir = '东' | '南' | '西' | '北'

    type Dir2 = Exclude<Dir, '北'> // 排除 '北'
    type Dir3 = Extract<Dir, '东'|'西'> // 只要 '东' '西'
  5. Omit(排除key类型)&& Pick(提取key类型) => 后接对象类型

    1
    2
    3
    4
    5
    6
    7
    8
    interface User {
    id: string;
    name: string;
    age: number;
    }

    type God1 = Omit<User, 'age' | 'id'> // 排除age和id,God此时只有name属性
    type God2 = Pick<User, 'name'> // God2只要name属性
  6. ReturnType(返回值类型)

    1
    2
    3
    4
    5
    6
    // 获取函数返回值类型
    function f(a:number, b:number){
    return a+b
    }

    type A = ReturnType<typeof f>

TS泛型

1
2
3
4
5
type Add<T> = (a:T, b:T) => T

// 泛型的调用
const addN:Add<number> = (a,b) => a+b
const addS:Add<string> = (a,b) => a + '' + b

用泛型封装网络请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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){
const url = path + 's'
return axios.post<User>(url, {data:attrs})
},
delete(){},
update(){},
get(){},
getPage(){}
}
}

var userResource = createResource('/api/v1/user')
0%