TS类型计算

507 阅读4分钟

基础类型

boolean、number、string、数组、元组、枚举、any、void、null、undefined、never、object

# boolean
let isDone: boolean = false;

# number
let decLiteral: number = 6; 

# string
let name: string = "bob";

# 数组
let list: number[] = [1, 2, 3];

# 元组
let x: [string, number];

# 枚举
enum Color {Red, Green, Blue} 
let c: Color = Color.Green;

# any
let notSure: any = 4; 
notSure = "maybe a string instead";

# unknown
表示未知类型的值
let e: unknown;
e = 10
e = 'abc'
// unknown和any的区别在于:any类型的变量可以赋值给任意变量,unknown类型的变量不能直接赋值给其他变量
let s: string
if(typeof e === "string"){
    s = e;
}

# void
function warnUser(): void { 
    console.log("This is my warning message"); 
}

# nullundefined
let u: undefined = undefined; 
let n: null = null;

# never `never`类型表示的是那些永不存在的值的类型
// 返回never的函数必须存在无法达到的终点 
function error(message: string): never {
    throw new Error(message); 
}
// 一般在类型计算中使用比较多

# object `object`表示非原始类型
declare function create(o: object | null): void; 
create({ prop: 0 }); // OK 
create(null); // OK 
create(42); // Error 
create("string"); // Error 
create(false); // Error 
create(undefined); // Error

字面量类型

type Str = 'abc'

类型断言

let someValue1: any = "this is a string";
let strLength1: number = (<string>someValue1).length;

let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;

联合类型

联合类型表示一个值可以是几种类型之一

type UN = number | string | boolean
type TXT = 'A' | 'B'

交叉类型

image.png

type Interface1 = {
    id: number;
    name: string;
}
  
type Interface2 = {
    age: string;
}

// 多个type交叉计算,得到所有类型的属性组成的新类型
type IntersectionType = Interface1 & Interface2;

{
    id: number;
    name: string;
    age: string;
}

-------------------------------------------

type Interface1 = {
    id: number;
    name: string;
}
  
type Interface2 = {
    id: string;
}
 
 // 多个type交叉计算,得到所有类型的属性组成的新类型,如果有相同属性,则同名属性类型进行计算
type IntersectionType = Interface1 & Interface2;

{
    id: never;
    name: string;
}

类型语法基础

类型的条件判断

ts 类型的条件判断的语法是 条件 ? 分支1 : 分支2

type isNumber<T> = T extends number ? true : false

type res1 = isNumber<1> // true

type res2 = isNumber<'1'> // false

类型的遍历

type Person = {
  name: string
  age: number
}

type KeyofPersion = keyof Person // 'name' | 'age'

k in keyof Person

infer 类型变量

type Str<T extends string> = T extends `${infer Key}=${infer Value}`
  ? {
      [k in Key]: Value
    }
  : never

image.png

索引类型

所谓的”索引类型“,就是用一个类型的所有字段名,组成一个字面量类型

type Person = {
  name: string
  age: number
}

映射类型

映射类型就是用于构造新的索引类型

type Record<K extends string | number | symbol, T> = { 
    [P in K]: T; 
}

// 将索引类型的key变为只读
type Readonly<T> =  {
  readonly [Key in keyof T]: T[Key];
}

// 将索引类型的key变为可选
type Partial<T> = {
  [Key in keyof T]?: T[Key]
}

// 将索引类型的key变为必选
type Required<T> = {
  [Key in keyof T]-?: T[Key]
}

重映射

重映射就是在索引后加一个 as 语句,表明索引转换成什么,它可以用来对索引类型做过滤和转换 比如过滤出类型为 string 的索引: 返回 never 代表过滤掉,否则保留

type FilterString<T> = {
  [Key in keyof T as T[Keyextends string ? Keynever]: T[Key];
}

还可以对索引做转换,比如修改索引名
type Getters<T extends Record<anyany>> = {
  [Key in keyof T as `get${Capitalize<Key & string>}`]: T[Key];
}

模式匹配

字符串利用正则可以做模式匹配,例如:

'abc'.replace(/a(b)c/, '$1,$1,$1') // 'b,b,b'

Typescript 的类型也同样可以做模式匹配

type GetValueType<T> = T extends Promise<infer R> ? R : never

type PromiseType = Promise<string[]>

type Result = GetValueType<PromiseType>

// Result = string[]

我们通过 extends 对传入的类型参数 T 做模式匹配,其中 value 部分是需要提取的,通过 infer 类声明一个局部变量 R 来保存,如果匹配,就返回匹配到的 R,否则就返回 never 代表没匹配到

模式匹配应用场景

trim 是去掉前后的空格、制表符、换行符,那么就通过模式匹配取出后面的字符,通过 infer 放入新的变量返回就行

type LeftTrim<Str extends string> = 
    Str extends `${' ' | '\t' | '\n'}${infer Rest}` 
    ? LeftTrim<Rest> : Str

type RightTrim<Str extends string> = 
    Str extends `${infer Rest}${' ' | '\t' | '\n'}` 
    ? RightTrim<Rest> : Str
 
type Trim<Str extends string> = RightTrim<LeftTrim<Str>>

type res = Trim<'       abc          '>

//计算后结果:

image.png

获取函数参数类型和返回值类型

type GetParams<Func extends Function> = Func extends (
  ...params: infer Params
) => any
  ? Params
  : never

type res = GetParams<(a: number, b: string) => void>

// 计算后结果:

image.png

type GetReturnType<Func extends Function> = Func extends (
  ...params: any
) => infer R
  ? R
  : never

type res = GetReturnType<(a: number, b: string) => Promise<number>>
// 计算后结果:

image.png

类型计算示例

实现一个ParseQueryString,要求输入"a=1&a=2&b=3&d=4", 输出

{ a: ["1","2"], b: "3", d: "4" }

WechatIMG3.jpeg

  1. 首先定义ParseQueryString,判断传入类型是否为 “&” 拼接格式的字面量类型,用第一个匹配到的 “&” 符号分割,第一部分为 "a=1" 格式, 使用 ParamString 处理,“&”后面的部分通过递归继续处理, 并使用MergeParams 合并数据
type ParseQueryString<Str extends string> =
  Str extends `${infer Param}&${infer Reset}`
    ? MergeParams<ParamString<Param>, ParseQueryString<Reset>>
    : ParamString<Str>
  1. 将匹配到的 "a=1" 形式的字面量类型处理为对象形式的索引类型 { "a" : "1" }
type ParamString<Str extends string> = Str extends `${infer Key}=${infer Value}`
  ? {
      [K in Key]: Value
    }
  : object

3 .将ParamString处理好的类型合并为一个索引类型

type MergeParams<OneParams extends object, OtherParam extends object> = {
  [Key in keyof OneParams | keyof OtherParam]: Key extends keyof  
    ? Key extends keyof OtherParam
      ? MergeValue<OneParams[Key], OtherParam[Key]>
      : OneParams[Key]
    : Key extends keyof OtherParam
      ? OtherParam[Key]
      : never
}
  1. 如果遇到key相同的值,则处理成数组格式
type MergeValue<One, Other> = One extends Other
  ? One
  : Other extends unknown[]
  ? [One, ...Other]
  : [One, Other]
  
 type s = ParseQueryString<'a=1&a=2&b=3&c=4'>

image.png