You don't have javascript enabled. Good luck! :(

欢迎页

Welcome to Ganace's Blog


欢迎您来到Ganace 的个人博客。

在茫茫互联网的海洋里能够遇见,实在是一份不小的幸运。

诚挚地向您问候一声:您好,朋友!

这里是属于Ganace个人的隐秘小空间,记录了工作学习中遇到的知识点与灵感,以及生活中的碎碎念与吐槽突如其来的中二病尬尬的文艺时间锻炼腹肌的时刻惊喜的小发现等等。

想要在自己的平淡无奇的人生长河中留下些什么,

或者只是为了拯救老人家岌岌可危的记忆力,

仅此而已。

来到此地,分享与你。

也期待与您的再次相遇!

Share with Me


有任何的分享或者建议与吐槽,都可以Email我:ganace@foxmail.com

欢迎您的来信!

【TypeScript】TypeScript类型体操(三) 中等挑战

  Mar 19, 2024     Ganace     Front-end-Foundation

TypeScript

TypeScript 的类型体操笔记,温故知新。

获取函数返回类型 ReturnType 实现 Omit 对象部分属性只读 Readonly

对象属性只读(递归) DeepReadonly 元组转合集 TupleToUnion 可串联构造器


中等挑战

一、获取函数返回类型 ReturnType<T>

不使用 ReturnType 实现 TypeScriptReturnType<T> 泛型。
type MyReturnType<T extends (...args: any) => unknown> = T extends (...args: any) => infer R ? R : never;
TypeScript提供的工具类型:

ReturnType<Type> 构造一个由函数 Type 的返回类型组成的类型。

二、实现 Omit Omit<T, K>

不使用 Omit 实现 TypeScriptOmit<T, K> 泛型。

Omit 会创建一个省略 K 中字段的 T 对象。

type MyOmit<T, K extends keyof T> = {
    [Key in keyof T as Key extends K ? never : Key]: T[Key];
};
TypeScript提供的工具类型:

Omit<Type, Keys> 通过从 Type 中选择所有属性然后删除 Keys(字符串字面或字符串字面的并集)来构造一个类型。

三、对象部分属性只读 Readonly<T>

实现一个泛型MyReadonly2<T, K>,它带有两种类型的参数TK

类型 K 指定 T 中要被设置为只读 (readonly) 的属性。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly<T>一样。

type MyReadonly2<T, K extends keyof T = keyof T> = Omit<T, K> & Readonly<Pick<T, K>>;
// 不使用内置方法
type MyReadonly2<T, K extends keyof T = keyof T> = {
    [P in keyof T as P extends K ? never : P]: T[P];
} & {
    readonly [P in K]: T[P];
};
TypeScript提供的工具类型:

Readonly<Type> 构造一个将 Type 的所有属性设置为 readonly 的类型,这意味着构造类型的属性不能重新分配。

四、对象属性只读(递归) DeepReadonly<T>

实现一个泛型 DeepReadonly<T>,它将对象的每个参数及其子对象递归地设为只读。

您可以假设在此挑战中我们仅处理对象。不考虑数组、函数、类等。但是,您仍然可以通过覆盖尽可能多的不同案例来挑战自己。

type DeepReadonly<T> = T extends Function ? T : { readonly [K in keyof T]: DeepReadonly<T[K]> };
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];
};
// from vue-next
type Primitive = string | number | boolean | bigint | symbol | undefined | null;
type Builtin = Primitive | Function | Date | Error | RegExp;
type DeepReadonly<T> = T extends Builtin
    ? T
    : T extends Map<infer K, infer V>
    ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
    : T extends ReadonlyMap<infer K, infer V>
    ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
    : T extends WeakMap<infer K, infer V>
    ? WeakMap<DeepReadonly<K>, DeepReadonly<V>>
    : T extends Set<infer U>
    ? ReadonlySet<DeepReadonly<U>>
    : T extends ReadonlySet<infer U>
    ? ReadonlySet<DeepReadonly<U>>
    : T extends WeakSet<infer U>
    ? WeakSet<DeepReadonly<U>>
    : T extends Promise<infer U>
    ? Promise<DeepReadonly<U>>
    : T extends {}
    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
    : Readonly<T>;

五、元组转合集 TupleToUnion<T>

实现泛型TupleToUnion<T>,它返回元组所有值的合集。
type TupleToUnion<T extends unknown[]> = T[number];
type TupleToUnion<T> = T extends (infer U)[] ? U : never;
export type TupleToUnion<T> = T extends Array<infer U> ? U : never;

六、可串联构造器

JavaScript 中我们经常会使用可串联(Chainable/Pipeline)的函数构造一个对象,但在 TypeScript 中,你能合理的给它赋上类型吗?

在这个挑战中,你可以使用任意你喜欢的方式实现这个类型 - Interface, Type 或 Class 都行。你需要提供两个函数 option(key, value) 和 get()。在 option 中你需要使用提供的 key 和 value 扩展当前的对象类型,通过 get 获取最终结果。

type Chainable<T = {}> = {
    option: <K extends string, V>(key: K extends keyof T ? never : K, value: V) => K extends keyof T ? Chainable<Omit<T, K> & Record<K, V>> : Chainable<T & Record<K, V>>;
    get: () => T;
};
TypeScript提供的工具类型:

Record<Keys, Type> 构造一个对象类型,其属性键为 Keys,其属性值为 Type。此实用程序可用于将一种类型的属性映射到另一种类型。