2018 年 10 月更新:@MattMcCutchen 发现它可以检测到readonly 字段(使下面被删除的段落无效),如this answer 所示。这是构建它的一种方法:
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},反之亦然。因此明显的 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} ?
所以这很好。
最后,我不知道您是否希望能够使用 public、private、protected 和 abstract 等纯类属性修饰符来做一些事情,但我会怀疑。碰巧private 和protected 类属性可以很容易排除,因为它们在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 在内的所有这些,您不能将它们添加到类型别名中的属性中(它们是仅限类的修饰符),因此我想不出太多的办法来触摸它们。
好的,希望对您有所帮助。