-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Labels
Description
题目
题目链接: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];
};
知识点
上下两种方法是一样的,这里讲左边的方法。
-
MapToKeys
作用把key = value
type T1 = MapToKeys<Person> // 结果 => T1 = { name: "name"; age?: "age" | undefined; }
-
FilterKeys
将可选的key
过滤掉type T2 = FilterKeys<Person>; // 结果 => T2 = string;
解析
{[K in keyof T]-?: undefined extends T[K] ? never : K}
keyof T
的结果是联合类型,这里是"name" | "age"
K in keyof T
中K
结果是name
或者age
-?
是去除可选项- 结果:
{ name: string; age: never; }
undefined extends number | undefined
⇒true
undefined extends string
⇒false
- 在
a
的结果上加[keyof T]
结果是string | never
⇒string
-
使用
MapToKeys
将Person
的key = value
,这样b
的结果就是name
-
{[K in 第二步的结果]: T[K]}
遍历取值,就得到最终的结果
tips
-?
将可选项去掉,让这个类型变成必选项,+?
增加可选项,让这个类型变成可选项- 为什么上面使用
undefined extends T[K]
,下面使用T[K] extends never
[K in keyof T]: string
- 可选项的值
K = string | undefined
- 必选项的值
K = string
- 可选项的值
[K in keyof T]: never
- 可选项的值
K = undefined
- 必选项的值
K = never
- 可选项的值
- 上面的
T[K]
要放到extends
右边,下面的T[K]
要放到extends
的左边
方法二
type GetRequired<T> = Pick<
T,
{ [key in keyof T]-?: {} extends Pick<T, key> ? never : key }[keyof T]
>;
知识点
思路和方法一是一样:
-
这里使用
Pick<T, key>
在T
中挑选出key
解析:
[key in keyof T]-?: {} extends Pick<T, key> ? never : key
{} extends {age?: number}
⇒true
{} extends {name: string}
⇒false
- 这里为什么不用
Pick<T, keyof T>
是因为keyof T
的结果是个联合类型,也就是说Pick<T, keyof T> = T
-
{[key in keyof T]-?: {} extends Pick<T, key> ? never : key}[keyof T]
结果是{name: string; age: never}["name" | "age"]
⇒string | never
⇒string
-
使用
Pick<T, 第二步的结果>
得到最终的结果
方法三
type GetRequired<T> = {
[key in keyof T as {} extends Pick<T, key> ? never : key]: T[key];
};
知识点
- 思路和方法二是一样的,使用
Pick<T, key>
在T
中挑选出key
{} extends Pick<T, key>
⇒true
说明空对象和纯可选是一致的- 通过
as
可以拆分成两部分,as
左边是结果,右边是约束条件- 当
key = age
时,{} extends Pick<Person, "age">
⇒true
⇒{[never]: Person["age"]}
⇒ 空 - 当
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];
};