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

typescript - แทนที่ประเภทวัตถุอย่างลึกด้วยประเภทวัตถุอื่นหากมีคีย์อยู่


ฉันจะแทนที่คุณสมบัติทั้งหมดของ B ด้วยค่าของ A ได้อย่างไร? และตัดคีย์ทั้งหมดของ A ที่ไม่มีใน B ออก

type A = {
  a: {
    a: string
    b: number
    c: boolean
    d: {
      a: any
      b: any
    }
  }
  b: { b: null }

}
type B = {
  a: {
    a: boolean
    b: boolean
    d: { a: boolean }
  }
}
// Result
type C = {
  a: {
    a: string
    b: number
    d: { a: any }
  }
}

แก้ไข :

นี่คือตัวอย่างว่าทำไมคำตอบของ @catgirlkelly จึงใช้ไม่ได้ในกรณีของฉัน

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


type Override<Source, Target> = Source extends object ? {
  [K in keyof Source]: K extends keyof Target ? Override<Source[K], Target[K]> : Source[K];
} : Target;


type QueryObj<T> = T extends object ? {
  [Key in keyof T]?: QueryObj<T[Key]>;
} : NonNullable<T> extends object ? never : boolean;


type GRAPH_QL_TYPE = {
  id: string,
  name: string,
  Assets?: {
    items: ({
      id: string,
      name: string,
    } )[],
  } ,
};


function query<Query extends QueryObj<GRAPH_QL_TYPE>>(query: Query) {
  // Doesn't work
  return {} as Override<Query, GRAPH_QL_TYPE>
  // Works
  // return {} as DeepReplace<Query, GRAPH_QL_TYPE>
}

const res = query({
  name: true,
  id: true,
  Assets: {
    items: [
      {
        name: true,
        id: true,
      }
    ]
  }
})
// res.Assets.items[0].name = true with Override, and it equals string with DeepReplace
const reactState: GRAPH_QL_TYPE = res

คำตอบ #1

การแปลงประเภทวัตถุเชิงลึกประเภทนี้มักจะมีกรณีขอบเขตมากมาย (เช่น คุณสมบัติที่ไม่จำเป็น ลายเซ็นดัชนี และประเภทสหภาพ) ดังนั้นใครก็ตามที่เข้ามาพร้อมกับคำถาม "เหมือนกัน" ควรระมัดระวังในการทดสอบคำตอบอย่างรอบคอบ นี่คือวิธีการหนึ่งที่เป็นไปได้:

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

DeepReplace<T, U> ทำการแทนที่ (ส่วนของ) T ด้วย (ส่วนของ) U อย่างซ้ำซ้อน

ถ้า T หรือ U ไม่ใช่วัตถุ จะคืนค่า U ตามเดิม นี่เป็นการตัดสินใจเฉพาะตัวและเป็นกรณีที่อาจเกิดขึ้นได้: เมื่อหนึ่งใน T หรือ U เป็นวัตถุและอีกตัวไม่ใช่ จะไม่ชัดเจน 100% ว่าพฤติกรรมที่ "ถูกต้อง" คืออะไร

หากไม่เช่นนั้น เราจะทำการแมปผ่านคุณสมบัติของ T เพื่อให้คีย์คุณสมบัติใด ๆ K ที่พบใน U ด้วย จะทำให้คุณสมบัติของ T ถูก DeepReplace ด้วยคุณสมบัติที่สอดคล้องกันจาก U นี่คือ "แกนหลัก" ของ DeepReplace<T, U> และน่าจะพบได้ในการใช้งานส่วนใหญ่

หากมีคีย์คุณสมบัติใดของ T ที่ ไม่ พบใน U คุณสมบัตินั้นจะคงไว้เช่นเดิมและจะไม่ถูกแทนที่ นี่เป็นอีกการตัดสินใจหนึ่งและอาจเป็นกรณีขอบเขตด้วยเช่นกัน


อย่างไรก็ตาม มาลองทดสอบมันกับโค้ดตัวอย่างของคุณกันเถอะ:

type C = DeepReplace<B, A>
/* type C = {
    a: {
        a: string;
        b: number;
        d: {
            a: any;
        };
    };
} */

ดังนั้นนี่จึงทำงานได้ตามที่ต้องการ เย้! และดูเหมือนว่ามันยังทำงานกับกรณีการใช้งานอื่นของคุณด้วย:

type Query = {
  name: true;
  id: true;
  Assets: {
    items: {
      name: true;
      id: true;
    }[];
  };
};

type GraphQLType = {
  id: string,
  name: string,
  Assets?: {
    items: ({
      id: string,
      name: string,
    })[],
  },
};

type QueryResult = DeepReplace<Query, GraphQLType>
/* type QueryResult = {
    name: string;
    id: string;
    Assets: {
        items: {
            name: string;
            id: string;
        }[];
    } | undefined;
} */

ดีใจสองเท่า ประเภทที่ต้องการอย่างแม่นยำรอบๆ คุณสมบัติที่เป็นทางเลือกและยูเนียนกับ undefined ไม่ชัดเจนสำหรับฉัน 100% แต่ก็อีกครั้ง มันขึ้นอยู่กับรายละเอียดเฉพาะของกรณีการใช้งาน

ลิงก์ Playground ไปยังโค้ด