1. ホーム
  2. typescript

1つのプロパティの設定を必要とするPartialライクなものを作成する方法

2023-10-04 05:04:53

質問

以下のような構造になっています。

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 のような、しかし上記の私の構造のように動作するジェネリックを作成するにはどうしたらよいでしょうか?

どのように解決するのですか?

私はあなたのための解決策を持っていると思います。 あなたが探しているものは、型をとる T を含む関連した型を生成するものを探しています。 を少なくともひとつ のプロパティを含む T . つまり、次のようなものです。 Partial<T> と似ていますが、空のオブジェクトを除外しています。

もしそうなら、ここに

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

分解してみると:まず AtLeastOne<T>Partial<T> と交差している。 何か . 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

さて、これでうまくいきましたか? がんばってください。