web-dev-qa-db-ja.com

単一のプロパティを設定する必要があるPartial-likeを作成する方法

次のような構造があります。

export type LinkRestSource = {
    model: string;
    rel?: string;
    title?: string;
} | {
    model?: string;
    rel: string;
    title?: string;
} | {
    model?: string;
    rel?: string;
    title: string;
};

それは言うこととほとんど同じです

type LinkRestSource = Partial<{model: string, rel: string, title: string}>

これは空のオブジェクトが渡されることを許可しますが、最初のタイプはプロパティの1つが渡される必要があることを除いて

Partialのようなジェネリックをどのように作成できますか?しかし、それは上記の私の構造のように動作しますか?

22
Juan Mendes

私はあなたのための解決策を持っていると思います。タイプTを取り、T。つまり、Partial<T>に似ていますが、空のオブジェクトは除外されます。

もしそうなら、ここにあります:

type AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U]

それを分析するには:まず、AtLeastOne<T>Partial<T>somethingと交差します。 U[keyof U]は、Uのすべてのプロパティ値の和集合であることを意味します。また、U(のデフォルト値)を マップされたタイプ として定義しました。ここで、Tの各プロパティは、単一のPick<T, K>にマップされますキーのプロパティタイプK。 (たとえば、Pick<{foo: string, bar: number},'foo'>{foo: string}と同等です...元の型から'foo'プロパティを「選択」します。)この場合のU[keyof U]が共用体であることを意味します。 Tからのすべての可能な単一プロパティタイプの。

うーん、それは混乱するかもしれません。次の具象型でどのように動作するかを段階的に見てみましょう。

type FullLinkRestSource = {
  model: string;
  rel: string;
  title: string;
}

type LinkRestSource = AtLeastOne<FullLinkRestSource>

それは

type LinkRestSource = AtLeastOne<FullLinkRestSource, {
  [K in keyof FullLinkRestSource]: Pick<FullLinkRestSource, K>
}>

または

type LinkRestSource = AtLeastOne<FullLinkRestSource, {
  model: Pick<FullLinkRestSource, 'model'>,
  rel: Pick<FullLinkRestSource, 'rel'>,
  title: Pick<FullLinkRestSource, 'title'>
}>

または

type LinkRestSource = AtLeastOne<FullLinkRestSource, {
  model: {model: string},
  rel: {rel: string},
  title: {title: string}>
}>

または

type LinkRestSource = Partial<FullLinkRestSource> & {
  model: {model: string},
  rel: {rel: string},
  title: {title: string}>
}[keyof {
  model: {model: string},
  rel: {rel: string},
  title: {title: string}>
}]

または

type LinkRestSource = Partial<FullLinkRestSource> & {
  model: {model: string},
  rel: {rel: string},
  title: {title: string}>
}['model' | 'rel' | 'title']

または

type LinkRestSource = Partial<FullLinkRestSource> &
  ({model: string} | {rel: string} | {title: string})

または

type LinkRestSource = {model?: string, rel?: string, title?: string} & 
  ({model: string} | {rel: string} | {title: string})

または

type LinkRestSource = { model: string, rel?: string, title?: string } 
  | {model?: string, rel: string, title?: string} 
  | {model?: string, rel?: string, title: string}

これはあなたが望むものだと思います。

あなたはそれをテストすることができます:

const okay0: LinkRestSource = { model: 'a', rel: 'b', title: 'c' }
const okay1: LinkRestSource = { model: 'a', rel: 'b' }
const okay2: LinkRestSource = { model: 'a' }
const okay3: LinkRestSource = { rel: 'b' }
const okay4: LinkRestSource = { title: 'c' }

const error0: LinkRestSource = {} // missing property
const error1: LinkRestSource = { model: 'a', titel: 'c' } // excess property on string literal

それで、それはあなたのために働きますか?幸運を!

22
jcalz

必要なプロパティがわかっている場合は、別の解決策があります。

AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>

これにより、あるタイプの複数のキーをロックすることもできます。 AtLeast<T, 'model' | 'rel'>

1
aegatlin

多分そのようなもの:

type X<A, B, C> = (A & Partial<B> & Partial<C>) | (Partial<A> & B & Partial<C>) | (Partial<A> & Partial<B> & C);
type LinkRestSource = X<{ model: string }, { rel: string }, { title: string }>
var d: LinkRestSource = {rel: 'sdf'};  

しかし、少し厄介です:)

または

type Y<A, B, C> = Partial<A & B & C> & (A | B | C);
0
cevek

Jcalzによるソリューションの簡単なバージョン:

type AtLeastOne<T> = { [K in keyof T]: Pick<T, K> }[keyof T]

したがって、実装全体は

type FullLinkRestSource = {
  model: string;
  rel: string;
  title: string;
}

type AtLeastOne<T> = { [K in keyof T]: Pick<T, K> }[keyof T]
type LinkRestSource = AtLeastOne<FullLinkRestSource>

const okay0: LinkRestSource = { model: 'a', rel: 'b', title: 'c' }
const okay1: LinkRestSource = { model: 'a', rel: 'b' }
const okay2: LinkRestSource = { model: 'a' }
const okay3: LinkRestSource = { rel: 'b' }
const okay4: LinkRestSource = { title: 'c' }

const error0: LinkRestSource = {} // missing property
const error1: LinkRestSource = { model: 'a', titel: 'c' } // excess property on string literal

これを試すためのTSプレイグラウンドリンクは次のとおりです

0
gafi

これはPartial<T>では不可能です。内部的には次のようになります。

type Partial<T> = { [P in keyof T]?: T[P]; };

すべてのプロパティがオプションになりました。

型システムを介してルールを強制することは可能(または簡単)であるとは思えません。

同様の方法でkeyofを使用する型を作成しようとする可能性がありますが、デフォルトのコンストラクターに条件があります。

Partialのようなタイプを宣言する方法を考えることができる場合、それぞれの異なるキーに対して?を放出するタイプのマトリックスを構築し、|を使用してそれらすべてを連結します最初の例では、ルールシステムタイプシステムを適用できる場合があります。

keyofに関するこのブログ投稿 は、いくつかのアイデアを与えるかもしれません。

0