# 过滤可选属性
// 获取可选属性
type GetOptional<T extends Record<PropertyKey, any>> = {
[K in keyof T as T[K] extends Required<T>[K] ? never : K]: T[K]
}
// 获取必选属性
type GetReqiured<T extends Record<PropertyKey, any>> = {
[K in keyof T as T[K] extends Required<T>[K] ? K : never]: T[K]
}
2
3
4
5
6
7
8
解析:对于T[K] extends Required<T>[K],如果当前K是可选属性,T[K]类型为string | undefined,那么Required<T>[K]的类型为string,string类型的值可以赋值给string | undefined,反之则不行。
泛型约束:T extends Record<PropertyKey, any>,表示传入的只能是对象类型(any和never也可以)。
另外实现方式:
type GetOptional<T extends Record<PropertyKey, any>> = Pick<T, Exclude<{
[K in keyof T]: T extends Record<K, T[K]> ? never : K
}[keyof T], undefined>>
type GetReqiured<T extends Record<PropertyKey, any>> = Pick<T, Exclude<{
[K in keyof T]: T extends Record<K, T[K]> ? K : never
}[keyof T], undefined>>
2
3
4
5
6
7
解析:
K in keyof T遍历类型T的所有键,通过Record来单独标记每个K的类型,Record<K, T[K],例如name得到的是{ name: string }。然后通过条件类型
extends判断传入的泛型T能不能赋值给Record生成的类型。interface Person { name: string; age: number; sex?: string; job?: string; } type P1 = { name: string; } type P2 = { job: string; }1
2
3
4
5
6
7
8
9
10
11
12Record生成的都是必选属性,所以Person的实例无法赋值给P2的实例(string | undefined不能分配给string),即可选属性生成的类型,此时有条件选择变成never。然后通过
keyof T取值,[K in keyof T]: T extends Record<K, T[K]> ? K : never }[keyof T]得到的最终就是必选属性组成的联合类型,此处为'name' | 'age'。通过
Exclude剔除其中的undefined。最终在类型
T中选出必选属性组成新的类型,达到过滤属性目的。
# 判断两个类型是否相等
相等判断:
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false
如果有两个条件类型T1 extends U1 ? X1 : Y1和T2 extends U2 ? X2 : Y2,它们被认为是相关的,需要满足以下条件:
T1和T2中的一个与另一个相关。这意味着T1可以赋值给T2或者T2可以赋值给T1。U1和U2是相同的类型。X1可以赋值X2。Y1可以赋值Y2。
上面Equal类型中,两个函数的泛型T都没有类型约束(也可以加上相同的类型约束,效果是一样的),说明两个是可以互相赋值的,因此如果需要满足第一个条件类型可以赋值给第二个条件类型,必须满足剩余条件2,即X与Y两个类型必须相等。
microsoft/src/compiler/checker.ts (opens new window)
不相等判断:
type NotEqual<X, Y> = true extends Equal<X, Y> ? false : true
# any类型判断
// https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
type IsAny<T> = 0 extends 1 & T ? true : false
// type IsAny<T> = number extends T & string ? true : false
// 不是any
type NotAny<T> = true extends IsAny<T> ? false : true
2
3
4
5
6
0 extends 1不满足类型约束(0不可分配给1),因此0 extends (1 & T)也不满足,因为(1 & T)应该比1更窄。然而,当T是any时,0 extends (1 & any)等价于0 extends any,这是满足的,这是因为any是故意不健全的,并且充当几乎所有其他类型的超类型和子类型。
type IsAny1<T> = T extends any ? true : false;
// 所有值都返回true
type IsAny2<T> = any extends T ? true : false;
// any返回true,但是T对于其他类型,any可以是任何东西,无法决定返回哪一个,所以最后返回boolean
2
3
4
5
# never类型判断
type IsNever<T> = [T] extends [never] ? true : false
// https://github.com/microsoft/TypeScript/issues/31751
type MakesSense = never extends never ? 'yes' : 'no' // Resolves to 'yes'
type ExtendsNever<T> = T extends never ? 'yes' : 'no'
type MakesSenseToo = ExtendsNever<{}> // Resolves to 'no'
type Huh = ExtendsNever<never> // Expect to resolve to 'yes', actually resolves to never
2
3
4
5
6
7
ExtendsNever是分配条件类型,条件类型分布在联合类型上。如果T是一个联合类型,ExtendsNever则将其应用于联合的每个成员,结果是所有应用程序的联合(ExtendsNever<'a' | 'b'> == ExtendsNever<'a' > | ExtendsNever<'b'>)。never是空联合类型(即没有成员的联合)。这是通过它在联合类型'a' | never == 'a'中的行为暗示了这一点。因此,当分配到never时,ExtendsNever永远不会应用,因为该联合类型中没有成员,因此结果为never。而[T]则可以禁止联合类型的分发。
# JSON.parse类型推断
为JSON对象提供类型重载:
// type JSONString<T> = string & T;
interface JSONString<T> extends String {
_: never;
}
interface JSON {
parse<T>(text: JSONString<T>, reviver?: (this: any, key: string, value: any) => any): T;
stringify<T>(value: T, replacer?: (this: any, key: string, value: any) => any, space?: string | number): JSONString<T>;
stringify<T>(value: T, replacer?: (number | string)[] | null, space?: string | number): JSONString<T>;
}
2
3
4
5
6
7
8
9
10
如果JSONString定义为type JSONString<T> = string & T;,会存在直接给JSONString赋值字符串,导致parse操作得到unknown类型。