ค้นหาเว็บไซต์

Recursive Partial<T> ใน TypeScript


ฉันมีอินเทอร์เฟซนี้:

export interface UserSettings
{
    one: {
        three: number;
        four: number;
    };
    two: {
        five: number;
        six: number;
    };
}

...และต้องการเปลี่ยนมันให้เป็นแบบนี้:

export interface UserSettingsForUpdate
{
    one?: {
        three?: number;
        four?: number;
    };
    two?: {
        five?: number;
        six?: number;
    };
}

...แต่ Partial<UserSettings> ผลลัพธ์ที่ได้คือ:

{
    one?: {
        three: number;
        four: number;
    };
    two?: {
        five: number;
        six: number;
    };
}

สามารถใช้ mapped types เพื่อทำให้ทุก property ในทุกระดับเป็น optional ได้หรือไม่ หรือว่าฉันต้องสร้าง interface ด้วยตัวเองสำหรับเรื่องนั้น?

คำตอบ #1

ด้วยการมาถึงของ Conditional Types ในเวอร์ชัน 2.8 ตอนนี้เราสามารถประกาศประเภทย่อยแบบเรียกซ้ำได้ดังนี้

type RecursivePartial<T> = {
  [P in keyof T]?:
    T[P] extends (infer U)[] ? RecursivePartial<U>[] :
    T[P] extends object | undefined ? RecursivePartial<T[P]> :
    T[P];
};

แหล่งอ้างอิง:

http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html

คำตอบ #2

คุณสามารถสร้างประเภทแผนที่ของคุณเองได้แบบนี้:

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

โชคร้าย นี่ไม่สามารถใช้ได้กับฟิลด์ที่มีประเภทเป็นอาเรย์ ดูเหมือนว่าจะยังไม่มีวิธีในการทำแมปประเภทตามเงื่อนไขเช่นกัน; เช่น จำกัดเฉพาะประเภทพื้นฐาน ดูที่ https://github.com/Microsoft/TypeScript/pull/12114#issuecomment-259776847 ตอนนี้เป็นไปได้แล้ว ดูคำตอบอื่น ๆ

คำตอบ #3

ฉันได้สร้างห้องสมุดชื่อ tsdef ที่มีรูปแบบ/โค้ดตัวอย่างทั่วไปหลายแบบเช่นนี้

สำหรับกรณีนี้ คุณสามารถใช้มันได้ดังนี้:

import { DeepPartial } from 'tsdef';

let o: DeepPartial<{a: number}> = {};

บทความที่เกี่ยวข้อง