TypeScript条件类型 - 过滤掉只读属性/仅选择必需的属性

Dan*_*elM 10 typescript conditional-types typescript2.8

在TypeScript中使用新的条件类型(或者可能是另一种技术),有没有办法根据修饰符从界面中选择某些属性?例如,有......

interface I1 {
    readonly n: number
    s: string
}
Run Code Online (Sandbox Code Playgroud)

我想创建一个基于前一个类型的新类型,如下所示:

interface I2 {
    s: string
}
Run Code Online (Sandbox Code Playgroud)

jca*_*alz 28

更新2018-10:@MattMcCutchen已经想通了,这可以检测readonly场(以下无效被撞击出通道),如图这个答案.这是一种构建它的方法:

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];
Run Code Online (Sandbox Code Playgroud)

如果要从界面中提取可写字段,可以一起使用上述WritableKeys定义Pick:

interface I1 {
    readonly n: number
    s: string
}

type I2 = Pick<I1, WritableKeys<I1>>; 
// equivalent to { s: string; }
Run Code Online (Sandbox Code Playgroud)

万岁!

因为readonly,我认为你不能提取那些.我以前看过这个问题,然后就不可能了; 而且我认为没有任何改变.

由于编译器没有合理地检查readonly属性,因此您始终可以将a分配{readonly n: number}给a {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 {} 
Run Code Online (Sandbox Code Playgroud)

但你不能.在最初命名为" readonly修饰符是一个笑话"GitHub问题中有一些有趣的讨论.

抱歉! 祝好运.


对于可选属性,您确实可以检测它们,从而提取或排除它们.这里的见解是{}延伸{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} 
Run Code Online (Sandbox Code Playgroud)

这很好.


最后,我不知道,如果你希望能够做的东西,唯一的类属性修改器一样public,private,protected,和abstract,但我对此表示怀疑.碰巧可以很容易地排除privateprotected类属性,因为它们不存在于: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} 
Run Code Online (Sandbox Code Playgroud)

但是,提取privateprotected性质可能是不可能的,因为同样的原因,排除他们是如此的简单:keyof Foo没有它们.对于所有这些,包括abstract,你不能将它们添加到类型别名的属性(它们是仅类修饰符),所以我没有想到要触摸它们.


好的,希望有所帮助.

  • 如果将来有人发现这个帖子,那么通过滥用条件类型的可分配性规则,_is_ [检查只读字段的方法](/sf/answers/3673117591/).(我想在理论上我应该在这里发一个答案,因为这个问题是最受欢迎的,然后将另一个问题关闭为重复?我不能被打扰.) (3认同)