Skip to content

前言

新坑,TS类型编程学习

知识记录

any 和 unknown区别

any可以接受任何值,也可以赋值 unknown可以接受任何值,但是不能赋值给any和unknown之外的类型

keyof 用于把索引类型变成联合类型

ts
interface Person {
    name: string
    age: number;
}
type Test = keyof Person // "name" | "age"
type Test1 = "name" extends keyof Person ? true : false // true
const a: Test = "name"

extends 详解

满足下面条件,判断为true

  1. 两边类型都是常量时,常量需要相同
  2. 两边都是基础类型时,类型需要相同
  3. 索引类型,需要操作符左边包含操作符右边
  4. 联合类型,满足其中一个即可。
  5. 交叉类型需要全部满足
ts
interface Pig {
    type: 'pig',
    weight: number
}
interface Animal {
    type: string
}

type Test = Pig extends Animal ? true : false // true

联合类型和交叉类型

联合类型

  1. 针对基础类型时,表示的是几个类型任意一个。
  2. 联合类型只能调用共同方法

交叉类型

  1. 针对基础类型和常量类型
ts
string & boolean // never
string & string // string
string & (string | boolean) // string
  1. 针对索引类型, 表示的索引合集,需要同时满足所有。
ts
interface A {
    name: string
}
interface B {
    age: number
}

type Test = A & B; // A需要同时具有name 和 age属性

工具类

IsEqual

ts
// 简单版本
type IsEqual1<A, B> = A extends B ? B extends A ? true : false : false 
type Test = IsEqual1<string, string>  // true
type Test1 = IsEqual1<string, '1'>  // false
type Test2 = IsEqual1<string, string | boolean> // boolean !!! 失效

// 升级版本
type IsEqual12<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false
type Test3 = IsEqual12<string, string>  // true
type Test4 = IsEqual12<string, '1'>  // false
type Test5 = IsEqual12<string, string | boolean> // false
type Test6 = IsEqual12<{
    name: string
}, {
   readonly name: string,
}>  // true  !!!!! 失败

// 最终版本
type IsEqual<A, B> = (<T>() => T extends A ? 1 : 2 ) extends (<T>() => T extends B ? 1 : 2) ? true : false
type Test7 = IsEqual<{
    name: string
}, {
   readonly name: string,
}>  // false

元组

提取元组的第一个数组类型

在线查看

ts
type GetCupleFirstType<T extends unknown[]> = T extends [infer FirstType, ...unknown[]] ? FirstType : never;
type Test = GetCupleFirstType<[string, number, symbol]>  // Test = string

提取元组的最后一个类型

在线查看

ts
type GetLastCupleType<T extends unknown[]> = T extends [...unknown[], infer Last] ? Last : never
type Test = GetLastCupleType<[string, number, symbol]>  // Test = symbol

数组Pop

在线查看

ts
type Pop<T extends unknown[]> = T extends [...infer Rest, unknown] ? Rest : never;
type Test = Pop<[string, boolean, symbol]>  // [string, boolean]

数组Push

ts
type Push<T extends unknown[], S> = [...T, S]
type Test = Push<[boolean], string> // [boolean, string]

数组Shift

ts
type Shift<T extends unknown[]> = T extends [any, ...infer Rest] ? Rest : never
type Test = Shift<[unknown, boolean, '1']>  // [boolean, '1']

数组Unshift

ts
type Unshift<T extends unknown[], S> = [S, ...T]
type Test = Unshift<[boolean], string> // [string, boolean]

数组concat

ts
type Concat<T extends unknown[], S extends unknown[]> = [...T, ...S]
type Test = Concat<[boolean, string], ['1', '2']> // [boolean, string, '1', '2']

数组zip

ts
type Zip<One extends [unknown, unknown], Other extends [unknown, unknown]> = One extends [infer OneFirst, infer OneAnother] ? Other extends [infer OtherFirst, infer OtherAnother] ? [[OneFirst, OtherFirst], [OneAnother, OtherAnother]]: [] : []
type Test = Zip<['1', '2'], ['3', '4']>  // [['1', '3'], ['2', '4']]

任意长度数组合并 zip2

ts
type Zip2<One extends unknown[], Second extends unknown[]> = One extends [infer OneFirst, ...infer OneRest] ? Second extends [infer SecondFirst, ...infer SecondRest] ? [[OneFirst, SecondFirst], ...Zip2<OneRest, SecondRest>] : [[OneFirst], ...Zip2<OneRest, []>] : Second extends [infer SecondFirst, ...infer SecondRest] ? [SecondFirst, ...Zip2<SecondRest, []>] : []

type Test = Zip2<["1", "2", "4"], ["5", "6", "7" ,"1"]>

数组反转

ts
type ReverseHelper<T extends unknown[], R extends unknown[] = []> = T extends [infer First, ...infer Rest] ? ReverseHelper<Rest, [First, ...R]> : R
type Reverse<T extends unknown[]> = ReverseHelper<T>

type Test = Reverse<['1']>

数组flat

ts
type ArrayFlat<T, R extends unknown[] = []> = T extends [infer First, ...infer Rest] ?  ArrayFlat<Rest, [...R, ...ArrayFlat<First>]> : [...R, ...(T extends unknown[] ? T : [T])]
type Test = ArrayFlat<[1| 5 | 2]> // 为啥是 1 | 5 | 2

字符串

字符串 startWith

ts
type StartWith<T extends string, S extends string> = T extends `${S}${string}` ? true : false  // ts只有字符串,没char,所以用string匹配
type Test = StartWith<"123", "1"> // true

字符串EndWith

ts
type EndWith<S extends string, T extends string> = S extends `${string}${T}` ? true : false;
type Test = EndWith<"123", "3"> // true
type Test1 = EndWith<"123", "4"> // false

字符串首位字符串

ts
type GetFirstString<T extends string> = T extends `${infer First}${string}` ? First : never // infer 定义的变量只能在true里面使用
type Test = GetFirstString<"123"> // "1"

替换字符串

ts
type Replace<S extends string, T extends string, R extends string> = S extends `${infer Prev}${T}${infer After}` ? `${Prev}${R}${After}` : S
type Test = Replace<"123", "2", "3"> // "133"
type Test1 = Replace<"123", "3", "2"> // "133"

创建指定长度的字符串

ts

字符串Trim

ts
type Space = '\n' | ' ' | '\t'
type TrimLeft<S extends string> = S extends `${Space}${infer Rest}` ? TrimLeft<Rest> : S
type TrimRight<S extends string> = S extends `${infer Rest}${Space}` ? TrimRight<Rest> : S
type Trim<S extends string> = TrimLeft<TrimRight<S>>

type Test = Trim<"  123\n\t">

首字母转大写

ts
type UpcaseFirst<S extends string> = S extends `${infer First}${infer Rest}` ? `${Uppercase<First>}${Rest}` : S;

type Test = UpcaseFirst<'ceshi'> // "Ceshi"

转小驼峰

ts
type CamelCase<S extends string> = S extends `${infer Prev}_${infer AfterFirst}${infer Rest}` ? `${Prev}${Uppercase<AfterFirst>}${CamelCase<Rest>}` : S
type A = CamelCase<'dong_test_liu'>

删除指定字符串

ts
type DropStr<S extends string, T extends string> = S extends `${infer Prefix}${T}${infer Suffix}` ? DropStr<`${Prefix}${Suffix}`, T> : S
type Test = DropStr<'123', '1'> // '23'

函数

获取函数参数类型

ts
type GetParamsType<F extends Function> = F extends (...args: infer Args) => any ? Args : []

type Test= GetParamsType<(a?: string) => any> // [a?: string]

获取函数返回参数类型

ts
type GetReturnType<F extends Function> = F extends (...args: any[]) => infer Return ? Return : never   // args中需要设置any,不能是unknow
type Test = GetReturnType<(a: string, b: number) => string> // string

添加函数参数

ts
type GetParamsType<F extends Function> = F extends (...args: infer Args) => any ? Args : [];
type GetReturnType<F extends Function> = F extends (...args: any[]) => infer Return ? Return : never;

type AddFunctionParams<F extends (...args: any[]) => any, T> = (...args: [...GetParamsType<F>, T]) => GetReturnType<F>;
type AddFunctionParams1<F extends (...args: any[]) => any, T> = F extends (...args: infer Args) => infer ReturnType ? (...args: [...Args, T]) => ReturnType : F
type Test = AddFunctionParams1<(a: string) => string, string>

设置函数中this的类型

通过函数参数中第一个参数this定义类型

ts
interface Person {
    name: string
    say(name: string): any
}

interface SayName {
    (this: Person): any
}

const a: SayName = function () {
    console.log(this.say('name is yang'))
}

const person: Person = {
    name: 'yang',
    say(name: string) {
        console.log(name)
    },
}
a() // Error
a.call(person)
const b = a.bind(person)
b()

获取函数中this指向的类型。

ts
type GetThisType<T extends Function> = T extends (this: infer ThisType, ...args: any[]) => any ? ThisType : never // 注意,函数参数要用 any[] 接受
type Test = GetThisType<(this: string, name: boolean) => boolean> // string

构造器和类实例

ts
interface Person {
    name: string
}

interface PersonCtor {
    new (name: string): Person
}

const createPerson: Person = function(ctor: PersonCtor)  {
    ctor() // 禁止 非构造函数调用
    return new ctor('name');
}

获取构造器的实例类型

ts
// 获取构造函数的函数实列
type GetInstanceType<T extends new (...args: any[]) => any > = T extends new (...args: any[]) => infer InstanceType ? InstanceType : unknown 

interface Person {
    name: string
}

interface PersonCtor {
    new (): Person
}

type Test = GetInstanceType<PersonCtor>  // Person

获取函数构造器参数

ts
// 获取构造函数的参数类型
type GetInstanceType<T extends new (...args: any[]) => any > = T extends new (...args: infer ParamsType) => any ? ParamsType : unknown 

interface Person {
    name: string
}
interface PersonCtor {
    new (name: boolean): Person
}

type Test = GetInstanceType<PersonCtor>  // [name: boolean]

获取索引类型中指定值的类型

ts
interface Person {
    name: boolean
    age: number
}

type GetPropNameType<T> = 'name' extends keyof T ? T extends { name: infer NameType } ? NameType : never : never

type Test = GetPropNameType<Person> // boolean

索引类型值修改

ts
type ChangeValue<T> = {
    [K in keyof T]: [T[K]]
}

interface Person {
    name: string
    age: number
}

type Test = ChangeValue<Person>

索引类型索引修改

ts
type ChangeIndex<T> = {
    [K in keyof T as Uppercase<K & string>]: [T[K]] // 只过滤 string类型, 交叉类型在针对普通类型时需要严格相同, 同时never索引默认不会展示
}

interface Person {
    name: string
    age: number
    1: number
}

type Test = ChangeIndex<Person> // {NAME: string, age: number}  无 1: number

Record

ts
type MyRecord<S extends  string | number | symbol, T> = {
    [K in S ]: T
} 

type A =  MyRecord<string | 1, number>

取出值为指定类型的索引

ts
type Mypick<Obj, T> = {
    [K in keyof Obj as Obj[k] extends T ? K : never] : Obj[K]
}
type A = {
    sex: number
}
type Test = Mypick<{
    name: string;
    age: number;
    sex: A
}, A | string>   // { name: string; sex: A }

readonly

ts
type MyReadOnly<T> = {
    readonly [K in keyof T]: T[K]
}

type Test = MyReadOnly<{
    name: string;
    age?: number
}>

ToPartial 全部可选

ts
type ToPartial<T> = {
    [K in keyof T]?: T[K]
}

type Test = ToPartial<{
    name: string
    age?: number
}>

去掉readonly

ts
type DropReadOnly<T extends Object> = {
    -readonly [K in keyof T]: T[K] 
}
type Test = DropReadOnly<{
    readonly name: string;
    age: number
}>

去掉可选

ts
type DropOptional<T extends Object> = {
    [K in keyof T]-?: T[K]
}
type Test = DropOptional<{
    name?: string;
    age: number
}>

获取指定索引类型的子集

ts
type FilterByIndexType<Obj extends Object, T extends number | string | symbol> = {
    [K in keyof Obj as K & T]: Obj[K]
}

type A = FilterByIndexType<{
    [x: symbol]: number
    name: string
    1: string
    2: number
},  symbol | 1>

type ReverseHelper<T extends unknown[], R extends unknown[] = []> = T extends [infer First, ...infer Rest] ? ReverseHelper<Rest, [First, ...R]> : R
type Reverse<T extends unknown[]> = ReverseHelper<T>

type Test = Reverse<['1']>type B = 1 & (number | string)

获取指定指针类型的子集

ts
type FilterValueType<Obj extends Object, T extends number | string | symbol> = {
    [K in keyof Obj as Obj[K] extends T ? K :never]: Obj[K]
}
type Test = FilterValueType<{
    name: string;
    age: 18
}, number>

循环

递归获取promise返回值类型

ts
type DeepPromiseTypeHelper<P> = P extends Promise<infer ValueType> ?  DeepPromiseTypeHelper<ValueType> : P
type DeepPromiseType<P extends Promise<unknown>> = DeepPromiseTypeHelper<P>

type Test = DeepPromiseType<Promise<number>>

反转

是否包含

ts
type IsEqual<A, B> = (<T>() => T extends A ? 1 : 2 ) extends (<T>() => T extends B ? 1 : 2) ? true : false
type Include<S extends unknown[], T> = S extends [infer First, ...infer Rest] ? IsEqual<First, T> extends true ? true :  Include<Rest, T> : false

type A = Include<['1' | '2', '2'], '2' | '1'>

pick、omit和Exclude

ts
type MyExclude<T extends keyof any, K extends keyof any> = T extends K ? never : T;
type test1 = MyExclude<1 | 2 | 3, 3> // 1 | 2 利用了分布式条件语句

type MyPick<T extends Record<keyof any, unknown>, K extends keyof T = keyof T> = {
    [P in K]: T[P]
}
type MyOmit<T extends Record<keyof any,unknown>, K extends keyof any> = MyPick<T, MyExclude<keyof T, K>>

type test = MyOmit<{
    age: string;
    name: number
}, 'age'>

getByPropType、getByPropValue

ts
type GetPropsByType<T extends object, K> = {
    [P in keyof T]: T[P] extends K ? P : never 
}[keyof T]

type PickByPropType<T extends object, K> = Pick<T, GetPropsByType<T, K>>
type OmitByPropType<T extends object,K> = Omit<T, GetPropsByType<T, K>>

互斥的联合类型

因为结构类型的存在,联合类型不能很好的做到互斥(复杂类型),比如下面

ts
type A = {
    age: string
}
type B = {
    name: number;
}
type C = A | B;

const test: C = {  // 无法做到只能A或者B
    age: '1';
    name: 1
}

// 改进如下
type WithOut<T, U> = {
    [K in Exclude<keyof U, keyof T>]: never; 
} &  T

元组和联合类型相互转换

T[number]

错题分析

写一个链式的对象

判断是否是联合类型

ts
type IsUnion<T, U = T> = [T] extends [never] ? false : ( // 单独一个never,使用包裹避免分布式条件语句
  T extends U ? Exclude<U, T> extends never ? false : true : true
);