typescript使用记录

29 阅读2分钟

2024.7.1 更

  1. 一个对象中必须存在 a 或者 b 属性,但是又不能同时存在
type AOrB = { a: string; b?: never } | { b: string; a?: never }

2024.2.18 更

  1. 重载: 方法同名,但是参数和返回值类型不一样
// 当传递了 key,返回对应 key 的值,没有传递 key,则返回整个对象
useGray(): Record<string, boolean>
useGray(key: string): boolean

useGray(key?: string): boolean | Record<string, boolean> {
  // 具体实现
}

上面这个需求用泛型就无法实现了,很多三方库也有类似的用法。

2024.2.8 更

  1. 字符串转数字
type StringToNumber<S extends string> = S extends `${infer N extends number}` ? N : number
  1. tsconfig配置项详解 其是对整个 tsconfig 所在文件夹及子目录生效
{
  "compilerOptions": {
    "module": "commonjs", // 用来指定要使用的模块化的规范,默认 commonjs
    "target": "es2017", // 需要兼容到的版本(取决于你想运行在什么浏览器上面),默认 es3
    "lib": ["ESNext"], // 指定要包含在编译中的库文件,默认 []
    "path": {
      "app/*": ["./src/app/*"]
    } // 将导入重新映射到其他查找位置
    // 其他规则...
  },
  "exclude": ["node_modules", "dist"], // 排除特定文件
  "extends": "./tsconfig.base.json",
}
  1. 可以使用 global.d.ts 在全局定义,也可以覆盖三方库中的定义 可以使用下面这种方式来声明,也可以使用 exportimport
/// <reference path="./types/xxx.d.ts" />

declare module 'xxx' {
  export type Module = {
    name: string
  }
}

类型关系

类型关系

类型之间的并集(|)会向上取顶部的类型。

never | 'a' => 'a'unknown | 'a' => 'unknown'

类型之间的交集(&)会向下取底部的类型。

never & 'a' = neverunknown & 'a' => 'a'

bottom Type: never,top Type: unknown,既是 top Type 也是 bottom Type: any

类型

  1. 联合类型
type Union = Name | Age
  1. 交叉类型
type Intersection = Name & Age
  1. 元组类型(TS 中的元组使用数组来表示)
type Turple = [Name, Age]

关键字

  1. keyof: 将对象中所有的键取出来组成联合类型,如果这个类型是基本类型的话,就是将基本类型中的方法取出来组成联合类型
// 'name' | 'age'
type Keys = keyof Union
// "toFixed" | "toExponential" | "toPrecision" | "toString" | "toLocaleString" | "valueOf"
type NumberKeys = keyof number
  1. extends: 判断左右的类型是不是小于等于右边的类型
type Extends<A, B> = A extends B ? true : false
  1. infer: 推断真正的类型
type First<T> = T extends [infer A] ? A : any
  1. in: 遍历所有的 key
type In = { [P in keyof Name]: Name[P] }
  1. as: 转换对应的 key
  2. `: 字符串
type As = { [P in keyof Name as `get${P}`]: Name[P] }

7: is: 缩小类型

declare function isString(val: unknown) val is string
  1. thisType: 推断方法中的 this 拥有哪些方法和属性 必须在 tsconfig.json 中配置 "noImplicitThis": true
type GetComputed<C> = C extends Record<string, (...args: any[]) => any>
  ? { [S in keyof C]: ReturnType<C[S]> }
  : never

declare function SimpleVue<D, C, M>(
  options: {
    data: (this: void) => D
    computed: C & ThisType<D>
    methods: M
  } & ThisType<D & M & GetComputed<C>>
): unknown

注意点:联合类型不需要使用 in 也是会触发其分发特性的

type MyExclude<T, U extends T> = T extends U ? never : T

逆变 / 协变

参数和返回值都应该比当前设置的类型更小。

常见技巧

获取数组长度

type Length<T extends unknown[]> = T['length']

联合两个数组

type Concat<A extends unknown[], B extends unknown[]> = [...A, ...B]

获取函数的返回值

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

元组、联合、交叉类型的互相转换

这个里面使用到了协变和逆变。

// 1. 元组转联合类型
type TurpleToUnion<T extends unknown[]> = T[number]
// 2. 联合类型转交叉类型
type UnionToIntersection<T> = (
  T extends object ? (k: T) => void : never
) extends (k: infer U) => void
  ? U
  : never
// 3. 联合类型转元组类型
type LastInUnion<U> = UnionToIntersection<
  U extends unknown ? (x: U) => 0 : never
> extends (x: infer L) => 0
  ? L
  : never
type UnionToTurple<T, L = LastInUnion<T>> = [T] extends [never]
  ? []
  : [...UnionToTurple<Exclude<T, L>>, L]

字符串转元组

type StringToTurple<T extends string> = T extends `${infer F}${infer L}`
  ? [F, ...StringToTurple<L>]
  : []

判断是不是 never/any

type IsNever<T> = [T] extends [never] ? true : false
type IsAny<T> = 0 extends (1 & T) ? true : false

判断两个数字的大小

type IsLess<
  A extends number,
  B extends number,
  I extends any[] = []
> = B extends I["length"]
  ? true
  : A extends I["length"]
  ? false
  : IsLess<A, B, [...I, 0]>

支持特定范围内的数字

type Range<
  Begin extends number,
  End extends number,
  I extends number[] = []
> = End extends I["length"]
  ? [I["length"]]
  : IsLess<I["length"], Begin> extends false
  ? Range<Begin, End, [...I, 0]>
  : [I["length"], ...Range<Begin, End, [...I, 0]>]

增加/减少修饰符

ReadonlyPartial

重载

function toString(x: string): string;
function toString(x: number): string;

// 实现签名(函数体具体实现)
function toString(x: string | number) {
  return String(x)
}

防御性编程

interface Foo { type: 'foo' }
interface Bar { type: 'bar' }
type All = Foo | Bar

function handleValue(val: All) {
  switch (val.type) {
    case 'foo':
      // 这里 val 被收窄为 Foo
      break
    case 'bar':
      // val 在这里是 Bar
      break
    default:
      // val 在这里是 never
      const exhaustiveCheck: never = val
      break
  }
}

引用

  1. 重学 TS