TS 常用方法

128 阅读2分钟

TypeScript Challenges 总结

常用方法

Concat

type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U];

Includes

type Includes<T extends unknown[], U> = T extends [infer F, ...infer R]
    ? Equal<F, U> extends true 
        ? true
        : Includes<R, U>
    : false;

ReadonlyPart

type ReadonlyPart<
    T extends Record<any, unknown>, 
    K extends keyof T = keyof T
> = Omit<T, K> & Readonly<Pick<T, K>>;

DeepReadonly

type DeepReadonly<T> = {
    readonly [K in keyof T]: T[K] extends Record<any, any>
    	? T[K] extends Function
            ? T[K]
            : DeepReadonly<T[K]>
        : T[K];
};

TupleLength

type TupleLength<T extends unknown[]> = T extends { length: infer P }
    ? P 
    : never;

// [][number] === never;

Split

type Split<S extends string, SEP extends string = ''> = string extends S
    ? string[]
    : S extends `${infer L}${SEP}${infer R}`
    ? [L, ...(R extends '' ? [] : Split<R, SEP>)]
    : SEP extends ''
    ? []
    : [S];

RemoveIndexSignature

type RemoveIndexSignature<T, P = PropertyKey> = {
    [K in keyof T as P extends K ? never : K extends P ? K : never]: T[K];
};

Reverse

type Reverse<T extends any[]> = T extends [infer L, ...infer R]
    ? [...Reverse<R>, L]
    : T;

ConstructTuple

/**
 * @link https://github.com/type-challenges/type-challenges/issues/11216
 * @link https://github.com/microsoft/TypeScript/issues/49459
 * 添加 0 extends 1 会将递归的限制从 999 提高很多,至 3300 多
 */
type ConstructTuple<
    L extends number,
    U extends any[] = []
> = 0 extends 1 
    ? false 
    : U['length'] extends L 
        ? U 
        : ConstructTuple<L, [...U, unknown]>;

DeepMutable

type DeepMutable<T extends object> = T extends object
    ? {
        -readonly [K in keyof T]: T[K] extends object
            ? T[K] extends Function
                ? T[K]
                : DeepMutable<T[K]>
            : T[K];
      }
    : T;

GetRequired

type GetRequired<T> = {
    [K in keyof T as T[K] extends Required<T>[K] ? K : never]: T[K];
};

GetOptional

type GetOptional<T> = {
    [K in keyof T as { [P in K]: T[K] } extends { [P in K]-?: T[K] }
        ? never
        : K]: T[K];
};

Equal

export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
    ? true
    : false;

LiteralUnion

/** https://github.com/Microsoft/TypeScript/issues/29729 */
export type LiteralUnion<T extends string> = T | (string & {});

Flatten

type Flatten<T> = {
    [K in keyof T]: T[K] extends object ? Flatten<T[K]> : T[K]
}

类型的转换

TupleToObject

type TupleToObject<T extends readonly PropertyKey[]> = {
    [K in T[number]]: K;
};
// type PropertyKey = string | number | symbol;

TupleToUnion

type TupleToUnion<T extends unknown[]> = T[number];

UnionToIntersection

/**
 * @link https://github.com/type-challenges/type-challenges/issues/10984
 */
type UnionToIntersection<T> = (
    T extends any ? (args: T) => unknown : never
) extends (args: infer U) => unknown
    ? U
    : never;

UnionToTuple

/**
 * @link [答案解析] https://github.com/type-challenges/type-challenges/issues/737
 * @link [逆变和协变] https://juejin.cn/post/7038073964958056478
 */
type UnionToIntersection<T> = (T extends T ? (x: T) => 0never) extends (
    x: infer P
) => 0 
    ? P 
    :never;

//  如果需要从重载中输出一种类型,TS 会选择重载中的最后一个签名
/**
 * @link https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html
 */
type LastInUnion<T> = UnionToIntersection<
    T extends T ? (x: T) => 0 : never
> extends (x: infer P) => 0
    ? P 
    : never;
    
type UnionToTuple<T, Last = LastInUnion<T>> = [T] extends [never]
    ? []
    : [...UnionToTuple<Exclude<T, Last>>, Last]

类型判断

IsNever

type IsNever<T> = [T] extends [never] ? true :false;

IsUnion

type IsUnion<T, U = T> = (
    T extends T ? (U extends T ? true : unknown) : never
) extends true 
    ? false 
    : true;

// type A = unknown extends true ? true : false; // false
// type B = never extends true ? true : false; // true
// type C = never extends never ? true : false; // true
// type D = never extends unknown ? true : false; // true
// type E = unknown extends never ? true : false; // false

IsTuple

/**
 * @link https://github.com/type-challenges/type-challenges/issues/14100
 * 元组和数组的区别在于,元组的长度是有限的,数组是无限的,也就是 T['length'] 返回的结果是不同的
 * 元组返回的是数字
 * 数组返回的是 number
 */
type IsTuple<T> = [T] extends [never]
    ? false
    : T extends readonly any[]
    ? number extends T['length']
            ? false
            : true
    : false;

IsAny

type IsAny<T> = 0 extends 1 & T ? true : false;