Skip to content

7. GetRequired #12

@astak16

Description

@astak16

题目

题目链接:GetRequired

实现 GetRequired ,返回所有的必需字段。

import type { Equal, Expect } from "@type-challenges/utils";

type cases = [
  Expect<Equal<GetRequired<{ foo: number; bar?: string }>, { foo: number }>>,
  Expect<
    Equal<GetRequired<{ foo: undefined; bar?: undefined }>, { foo: undefined }>
  >
];

答案

Person 类型举例

type Person = {
  name: string;
  age?: number;
};

方法一

type MapToKeys<T> = {
  [K in keyof T]: K;
};

type FilterKeys<T> = {
  [K in keyof T]-?: undefined extends T[K] ? never : K;
}[keyof T];

type GetRequired<T> = {
  [K in FilterKeys<MapToKeys<T>>]: T[K];
};
type MapToNever<T> = {
  [K in keyof T]: never;
};

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

type GetRequired<T> = {
  [K in PickNever<MapToNever<T>>]: T[K];
};

知识点

上下两种方法是一样的,这里讲左边的方法。

  1. MapToKeys 作用把 key = value

    type T1 = MapToKeys<Person>
    
    // 结果 =>
    
    T1 = {
      name: "name";
      age?: "age" | undefined;
    }
  2. FilterKeys 将可选的 key 过滤掉

    type T2 = FilterKeys<Person>;
    
    // 结果 =>
    
    T2 = string;

    解析

    1. {[K in keyof T]-?: undefined extends T[K] ? never : K}
      1. keyof T 的结果是联合类型,这里是 "name" | "age"
      2. K in keyof TK 结果是 name 或者 age
      3. -? 是去除可选项
      4. 结果: { name: string; age: never; }
        • undefined extends number | undefinedtrue
        • undefined extends stringfalse
    2. a 的结果上加 [keyof T] 结果是 string | neverstring
  3. 使用 MapToKeysPersonkey = value,这样 b 的结果就是 name

  4. {[K in 第二步的结果]: T[K]} 遍历取值,就得到最终的结果

tips

  1. -?将可选项去掉,让这个类型变成必选项,+? 增加可选项,让这个类型变成可选项
  2. 为什么上面使用 undefined extends T[K] ,下面使用 T[K] extends never
    1. [K in keyof T]: string
      1. 可选项的值 K = string | undefined
      2. 必选项的值 K = string
    2. [K in keyof T]: never
      1. 可选项的值 K = undefined
      2. 必选项的值 K = never
    3. 上面的 T[K] 要放到 extends 右边,下面的 T[K] 要放到 extends 的左边

方法二

type GetRequired<T> = Pick<
  T,
  { [key in keyof T]-?: {} extends Pick<T, key> ? never : key }[keyof T]
>;

知识点

思路和方法一是一样:

  1. 这里使用 Pick<T, key>T 中挑选出 key

    解析: [key in keyof T]-?: {} extends Pick<T, key> ? never : key

    1. {} extends {age?: number}true
    2. {} extends {name: string}false
    3. 这里为什么不用 Pick<T, keyof T> 是因为 keyof T 的结果是个联合类型,也就是说 Pick<T, keyof T> = T
  2. {[key in keyof T]-?: {} extends Pick<T, key> ? never : key}[keyof T] 结果是

    1. {name: string; age: never}["name" | "age"]string | neverstring
  3. 使用 Pick<T, 第二步的结果> 得到最终的结果

方法三

type GetRequired<T> = {
  [key in keyof T as {} extends Pick<T, key> ? never : key]: T[key];
};

知识点

  1. 思路和方法二是一样的,使用 Pick<T, key>T 中挑选出 key
  2. {} extends Pick<T, key>true 说明空对象和纯可选是一致的
  3. 通过 as 可以拆分成两部分,as 左边是结果,右边是约束条件
    1. key = age 时,{} extends Pick<Person, "age">true{[never]: Person["age"]} ⇒ 空
    2. key = name 时,{} extends Pick<Person, "name">false{["name"]: Person["name"]}{name: string}

方法四

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

// 等价于
type GetRequired<T, P extends Required<T> = Required<T>> = {
  [key in keyof T as T[key] extends P[key] ? key : never]: T[key];
};

// 等价于
type GetRequired<T> = {
  [key in keyof T as T[key] extends { [K in keyof T]-?: T[K] }[key]
    ? key
    : never]: T[key];
};

// 等价于
type MyRequired<T> = { [K in keyof T]-?: T[K] };
type GetRequired<T, P extends MyRequired<T> = MyRequired<T>> = {
  [key in keyof T as T[key] extends P[key] ? key : never]: T[key];
};

方法五

type PossiblyRequiredKeys<T> = {
  [K in keyof T]: undefined extends T[K] ? never : K;
}[keyof T];

type DefinitelyRequiredKeys<T, R extends T = Required<T>> = {
  [K in keyof R]: undefined extends R[K] ? K : never;
}[keyof R];

type GetRequired<T> = Pick<
  T,
  PossiblyRequiredKeys<T> | DefinitelyRequiredKeys<T>
>;

方法六

type GetRequired<T> = {
  [key in keyof T extends infer K
    ? K extends keyof T
      ? {} extends Pick<T, K>
        ? never
        : K
      : never
    : never]: T[key];
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions