web-dev-qa-db-ja.com

TypeScript条件型-読み取り専用プロパティを除外/必要なプロパティのみを選択

TypeScriptの新しい条件型(または別の手法)を使用して、修飾子に基づいてインターフェイスから特定のプロパティのみを選択する方法はありますか?たとえば、...

interface I1 {
    readonly n: number
    s: string
}

前のタイプに基づいて、次のような新しいタイプを作成したいと思います。

interface I2 {
    s: string
}
11
DanielM

更新2018-10: @ MattMcCutchen は、isreadonlyフィールドを検出できる(以下の削除されたパッセージを無効にする)ことが可能であると判断しました。 この回答 に示されています。これを構築する方法は次のとおりです。

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については、それらを抽出できないと思います。私は 以前にこの問題を見ました そしてそれは不可能でした;何も変わっていないと思います。

コンパイラはreadonlyプロパティを適切にチェックしません なので、いつでも{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 {} ????

しかし、できません。これについては、 元々「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} ????

いいですね。


最後に、publicprivateprotectedabstractなどのクラスのみのプロパティ修飾子を使用して処理を実行できるようにするかどうかはわかりませんが、疑わしいと思います。 privateおよびprotectedクラスのプロパティは、keyofに存在しないため、excludedになる可能性があります。

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} ????

ただし、extractprivateまたはprotectedプロパティは、除外するのが非常に簡単であるのと同じ理由で、不可能な場合があります。keyof Fooにはありません。そして、abstractを含むこれらすべてについて、型エイリアスのプロパティにそれらを追加することはできません(これらはクラスのみの修飾子です)ので、それらに触れるために私が考えることはあまりありません。


さて、それが役立つことを願っています。

26
jcalz