TypeScript: hlboká čiastková?


Existuje spôsob, ako v TypeScripte určiť čiastkový typ, ktorý zároveň všetky vnorené objekty robí čiastkovými? Napríklad:

interface Foobar {
  foo: number;
  bar: {
    baz: boolean;
    qux: string;
  };
}

const foobar: Partial<Foobar> = {
  foo: 1,
  bar: { baz: true }
};

Toto vyvolá nasledujúcu chybu:

TS2741: Property 'qux' is missing in type '{ baz: true; }' but required in type '{ baz: boolean; qux: string; }'.

Existuje nejaký spôsob, ako spraviť aj detské uzly čiastočnými?

Odpoveď č. 1

Jednoducho môžete vytvoriť nový typ, povedzme DeepPartial, ktorý sa v podstate odkazuje sám na seba (aktualizované január 2022, aby sa riešili možné ne-objekty):

type DeepPartial<T> = T extends object ? {
    [P in keyof T]?: DeepPartial<T[P]>;
} : T;

Potom ho môžete použiť takto:

const foobar: DeepPartial<Foobar> = {
  foo: 1,
  bar: { baz: true }
};

Pozrite si príklad dôkazu konceptu na TypeScript Playground.

Odpoveď č. 2

Ak hľadáte rýchle a jednoduché riešenie, pozrite si balík type-fest, ktorý obsahuje mnoho užitočných predpripravených typov TypeScript vrátane typu PartialDeep.

Pre technickejšie a prispôsobiteľné riešenie si pozrite túto odpoveď.

Odpoveď č. 3

Inšpiroval som sa odpoveďami na túto otázku, aby som vytvoril svoju vlastnú verziu PartialDeep.

Po ceste som narazil na niektoré problémy s vstavanými objektmi; pre môj prípad použitia by som neočakával, že objekt Date nebude mať niektoré zo svojich metód. Buď tam je, alebo nie je.

Tu je moja verzia:

// Primitive types (+ Date) are themselves. Or maybe undefined.
type PartialDeep<T> = T extends string | number | bigint | boolean | null | undefined | symbol | Date
  ? T | undefined
  // Arrays, Sets and Maps and their readonly counterparts have their items made
  // deeply partial, but their own instances are left untouched
  : T extends Array<infer ArrayType>
  ? Array<PartialDeep<ArrayType>>
  : T extends ReadonlyArray<infer ArrayType>
  ? ReadonlyArray<ArrayType>
  : T extends Set<infer SetType>
  ? Set<PartialDeep<SetType>>
  : T extends ReadonlySet<infer SetType>
  ? ReadonlySet<SetType>
  : T extends Map<infer KeyType, infer ValueType>
  ? Map<PartialDeep<KeyType>, PartialDeep<ValueType>>
  : T extends ReadonlyMap<infer KeyType, infer ValueType>
  ? ReadonlyMap<PartialDeep<KeyType>, PartialDeep<ValueType>>
  // ...and finally, all other objects.
  : {
      [K in keyof T]?: PartialDeep<T[K]>;
    };

Odpoveď č. 4

Jednoducho vytvorte nový typ DeepPartial

DeepPartial v podstate odkazuje na seba samého, keď je jeho vlastnosť typu object, aby aplikoval DeepPartial na vlastnosti tejto vlastnosti a tak ďalej

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}

Použitie

interface Foobar {
  foo: number;
  bar: {
    foo1: boolean;
    bar1: string;
  };
}

const foobar: DeepPartial<Foobar> = {
  foo: 1,
  bar: { foo1: true }
};

Príklad TS ihriska

Súvisiace články