TypeScript 条件类型 - 过滤掉只读属性/只选择必需的属性
TypeScript conditional types - filter out readonly properties / pick only required properties
使用 TypeScript 中的新条件类型(或者可能是另一种技术),有没有办法根据修饰符从接口中仅选择某些属性?例如,有...
interface I1 {
readonly n: number
s: string
}
我想在之前的类型的基础上创建一个新类型,如下所示:
interface I2 {
s: string
}
2018-10 更新:@MattMcCutchen has figured out that it is possible to detect readonly
fields (invalidating the struck-out passage below), as shown in 。这是一种构建它的方法:
type IfEquals<X, Y, A=X, B=never> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type WritableKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T];
type ReadonlyKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
}[keyof T];
如果要从接口中提取可写字段,可以使用上面的WritableKeys
定义和Pick
一起使用:
interface I1 {
readonly n: number
s: string
}
type I2 = Pick<I1, WritableKeys<I1>>;
// equivalent to { s: string; }
万岁!
对于 readonly
,我认为您无法提取这些。我已经 looked at this issue before 但那是不可能的;而且我认为没有任何改变。
自 the compiler doesn't soundly check readonly
properties 以来,您始终可以将 {readonly n: number}
分配给 {n: number}
和 vice-versa。因此,明显的 TSv2.8 条件类型检查不起作用。例如,如果 {n: number}
不被认为可分配给 {readonly n: number}
,那么您可以执行以下操作:
// does not work, do not try this
type ExcludeReadonlyProps<T> = Pick<T,
{ [K in keyof T]-?:
({ readonly [P in K]: T[K] } extends { [P in K]: T[K] } ? never : K)
}[keyof T]>
type I2 = ExcludeReadonlyProps<I1> // should be {s: string} but is {}
但是你不能。 GitHub issue originally named "readonly
modifiers are a joke" 中对此有一些有趣的讨论。
对不起!祝你好运。
对于可选属性,您确实可以检测到它们并因此提取或排除它们。这里的见解是 {}
扩展了 {a?: string}
,但 {}
没有扩展 {a: string}
甚至 {a: string | undefined}
。以下是如何构建一种从类型中删除可选属性的方法:
type RequiredKeys<T> = { [K in keyof T]-?:
({} extends { [P in K]: T[K] } ? never : K)
}[keyof T]
type OptionalKeys<T> = { [K in keyof T]-?:
({} extends { [P in K]: T[K] } ? K : never)
}[keyof T]
type ExcludeOptionalProps<T> = Pick<T, RequiredKeys<T>>
type I3 = {
a: string,
b?: number,
c: boolean | undefined
}
type I4 = ExcludeOptionalProps<I3>;
// {a: string; c: boolean | undefined}
很好。
最后,我不知道你是否希望能够使用 class-only 属性 修饰符,如 public
、private
、[=32] =],和 abstract
,但我对此表示怀疑。 private
和 protected
class 属性可以很容易地 排除 ,因为它们不存在于 keyof
中:
class Foo {
public a = ""
protected b = 2
private c = false
}
type PublicOnly<T> = Pick<T, keyof T>; // seems like a no-op but it works
type PublicFoo = PublicOnly<Foo>; // {a: string}
但是提取 private
或 protected
属性可能是不可能的,原因与排除它们非常容易一样:keyof Foo
没有它们。对于所有这些,包括 abstract
,您不能将它们添加到类型别名中的属性(它们是 class-only 修饰符),所以我想不出太多办法来触及它们。
好的,希望对您有所帮助。
使用 TypeScript 中的新条件类型(或者可能是另一种技术),有没有办法根据修饰符从接口中仅选择某些属性?例如,有...
interface I1 {
readonly n: number
s: string
}
我想在之前的类型的基础上创建一个新类型,如下所示:
interface I2 {
s: string
}
2018-10 更新:@MattMcCutchen has figured out that it is possible to detect readonly
fields (invalidating the struck-out passage below), as shown in
type IfEquals<X, Y, A=X, B=never> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type WritableKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T];
type ReadonlyKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
}[keyof T];
如果要从接口中提取可写字段,可以使用上面的WritableKeys
定义和Pick
一起使用:
interface I1 {
readonly n: number
s: string
}
type I2 = Pick<I1, WritableKeys<I1>>;
// equivalent to { s: string; }
万岁!
对于 readonly
,我认为您无法提取这些。我已经 looked at this issue before 但那是不可能的;而且我认为没有任何改变。
自 the compiler doesn't soundly check readonly
properties 以来,您始终可以将 {readonly n: number}
分配给 {n: number}
和 vice-versa。因此,明显的 TSv2.8 条件类型检查不起作用。例如,如果 {n: number}
不被认为可分配给 {readonly n: number}
,那么您可以执行以下操作:
// does not work, do not try this
type ExcludeReadonlyProps<T> = Pick<T,
{ [K in keyof T]-?:
({ readonly [P in K]: T[K] } extends { [P in K]: T[K] } ? never : K)
}[keyof T]>
type I2 = ExcludeReadonlyProps<I1> // should be {s: string} but is {}
但是你不能。 GitHub issue originally named "readonly
modifiers are a joke" 中对此有一些有趣的讨论。
对不起!祝你好运。
对于可选属性,您确实可以检测到它们并因此提取或排除它们。这里的见解是 {}
扩展了 {a?: string}
,但 {}
没有扩展 {a: string}
甚至 {a: string | undefined}
。以下是如何构建一种从类型中删除可选属性的方法:
type RequiredKeys<T> = { [K in keyof T]-?:
({} extends { [P in K]: T[K] } ? never : K)
}[keyof T]
type OptionalKeys<T> = { [K in keyof T]-?:
({} extends { [P in K]: T[K] } ? K : never)
}[keyof T]
type ExcludeOptionalProps<T> = Pick<T, RequiredKeys<T>>
type I3 = {
a: string,
b?: number,
c: boolean | undefined
}
type I4 = ExcludeOptionalProps<I3>;
// {a: string; c: boolean | undefined}
很好。
最后,我不知道你是否希望能够使用 class-only 属性 修饰符,如 public
、private
、[=32] =],和 abstract
,但我对此表示怀疑。 private
和 protected
class 属性可以很容易地 排除 ,因为它们不存在于 keyof
中:
class Foo {
public a = ""
protected b = 2
private c = false
}
type PublicOnly<T> = Pick<T, keyof T>; // seems like a no-op but it works
type PublicFoo = PublicOnly<Foo>; // {a: string}
但是提取 private
或 protected
属性可能是不可能的,原因与排除它们非常容易一样:keyof Foo
没有它们。对于所有这些,包括 abstract
,您不能将它们添加到类型别名中的属性(它们是 class-only 修饰符),所以我想不出太多办法来触及它们。
好的,希望对您有所帮助。