ทำไม @graphql-codegen/typescript ถึงกำหนดประเภท parent ของ resolver ให้เป็นประเภท output?
ฉันกำลังใช้ @graphql-codegen/typescript เพื่อสร้างประเภทสำหรับสคีม่า GraphQL นี้:
type Book {
title: String
author: String
comment: String
}
type Query {
books: [Book]
}
ข้อความตอนหนึ่งจากโค้ดที่สร้างขึ้น:
export type Book = {
__typename?: 'Book';
author?: Maybe<Scalars['String']['output']>;
comment?: Maybe<Scalars['String']['output']>;
title?: Maybe<Scalars['String']['output']>;
};
ประเภท TS "Book" คือประเภทที่ใช้สำหรับพ่อแม่ที่ถูกส่งไปยังตัวแก้ไข (resolvers) สำหรับฟิลด์ของประเภท GQL "Book"
ฉัน เข้าใจจริงๆ ว่าสมาชิกของชนิด GQL ที่ชื่อว่า "Book" ไม่ได้ถูกระบุว่าเป็นค่าไม่เป็นศูนย์ ดังนั้นการสืบค้นอาจได้รับค่า null เป็นผลลัพธ์สำหรับสมาชิกใด ๆ ของมัน และฉันก็เข้าใจด้วยว่าการสืบค้นใด ๆ อาจเลือกที่จะไม่รวมสมาชิกบางตัวในผลลัพธ์
อย่างไรก็ตาม สิ่งเหล่านี้ไม่เกี่ยวข้องที่นี่เพราะว่า TS type "Book" ไม่ใช่สิ่งที่ถูกส่งกลับในคำตอบ แต่เป็นสิ่งที่ถูกส่งไปยัง field resolvers เป็นข้อมูลนำเข้า "Book" ดูเหมือนว่าจะเป็น output type สำหรับ query แต่ถูกใช้เป็น resolver input type
ดังนั้น ฉันไม่เข้าใจว่าทำไมฟิลด์ใน Book จึงเป็นตัวเลือกและพิมพ์เป็น Maybe ฟิลด์ที่ถูกละเว้นจะไม่ได้เรียกใช้ตัวแก้ไข และฟิลด์ที่ผิดพลาดเกิดจากการคืนค่า null หรือการโยนข้อผิดพลาดในตัวแก้ไข ซึ่งทั้งสองกรณีไม่แม้แต่จะมีความหมายที่กำหนดไว้อย่างชัดเจนสำหรับ input ของตัวแก้ไข
เพื่อที่จะเข้าใจเพิ่มเติมว่าสิ่งใดที่ถูกส่งต่อไปมา ฉันได้ดำเนินการสร้างตัวแก้ไขสำหรับหนังสือดังนี้:
const books = [
{
title: 'The Awakening',
author: 'Kate Chopin',
},
{
title: 'City of Glass',
author: 'Paul Auster',
},
];
const resolvers: Resolvers = {
Query: {
books: () => books,
},
Book: {
title: (book: Book) => {
console.log("title", book);
return book.title!;
},
comment: (book: Book) => {
console.log("comment", book);
return "foobar";
}
},
};
ดังนั้นฉันกำลังส่งคืนออบเจ็กต์ที่มี title และ author แต่ไม่มี comment จาก parent resolver แล้วจึงสร้าง comment แบบ on-the-fly ในตัว resolver ของ "comment" ฉันยังบันทึกออบเจ็กต์ parent ด้วย โค้ดนี้ถูกเรียกใช้บน Apollo Server
ข้อสังเกต: วัตถุหลักที่ถูกส่งไปยัง field resolvers คือสิ่งที่ฉันส่งกลับมาจาก parent resolver กล่าวคือ ไม่มีฟิลด์ comment และมีทั้งฟิลด์ title และ author อยู่ แม้ว่าการสอบถามจริงจะไม่ได้ร้องขอฟิลด์เหล่านั้น
มันดูสมเหตุสมผลสำหรับฉันที่วัตถุ 'ภายใน' นี้จากตัวแก้ปัญหาของพ่อแม่จะถูกส่งต่อไปยังตัวแก้ปัญหาฟิลด์; นี่เป็นวิธีที่ฉันเข้าใจว่าตัวแก้ปัญหาทำงาน
สิ่งที่สับสนอย่างยิ่งก็คือ ตัวสร้างโค้ดสร้างประเภท Book ในสไตล์ของประเภท “ผลลัพธ์” โดยมีฟิลด์ทั้งหมดเป็นทางเลือก/Maybe และมีฟิลด์ที่สร้างขึ้นสำหรับตัวแก้ปัญหา อินพุต นั้น
ฉันกำลังพลาดอะไรอยู่หรือ?
แก้ไข: นี่คือบางส่วนของประเภทนิยามที่สร้างขึ้นเพิ่มเติม:
export type Maybe<T> = T | null;
export type Scalars = {
ID: { input: string; output: string; }
String: { input: string; output: string; }
Boolean: { input: boolean; output: boolean; }
Int: { input: number; output: number; }
Float: { input: number; output: number; }
};
คำตอบ #1
ในศัพท์ของ GraphQL ประเภทอินพุตจะถูกใช้เมื่อกำหนดอินพุตให้กับ mutation type ของ GraphQL ที่ถูกกำหนดในสคีมาของคุณและ type ของ GraphQL ของ parent ของตัวแปลงผลเหมือนกัน TypeScript กำลังวางคำศัพท์ของตัวเองบนทั้งหมดนี้ ดังนั้นมันอาจทำให้สับสนได้
ในกรณีนี้ codegen ได้กำหนดคีย์
inputและoutputที่เหมือนกันสำหรับประเภทScalarsของมัน ซึ่งยิ่งทำให้เกิดความสับสนมากขึ้น
A field resolver (เช่น ตัวแก้ปัญหาที่แก้ไขฟิลด์บนประเภท) ไม่รับรู้ถึง เนื้อหา ของข้อมูลต้นทางของคุณเกินกว่าสิ่งที่ ประเภท ของ GraphQL บอกมัน
ลองพิจารณาดูว่าจะเกิดอะไรขึ้นถ้าคุณเปลี่ยนค่าคงที่ books ของคุณเป็น:
const books = [
{
title: 'The Awakening',
author: 'Kate Chopin',
},
{
title: 'City of Glass',
comment: 'Break in case of emergency',
},
];
สิ่งนี้จะโอเคถ้าฟิลด์ Book ของคุณทั้งหมดสามารถเป็นค่า null ได้ ชนิดของ TypeScript type ของพาเรนต์ไปยัง resolvers ก็ยังคงถูกต้องเช่นกัน
ตัวแก้ไขใช้ได้สูงสุด 4 อาร์กิวเมนต์:
resolverFunction: (parent,args,context,info) => {…code…}
ลายเซ็นของฟังก์ชันจะเหมือนกันไม่ว่าจะกำลังแก้ไข Query หรือฟิลด์สำหรับประเภทหนึ่ง ในกรณีแรก parent จะเป็น undefined ในกรณีหลัง parent จะเป็นวัตถุ parent และจะเป็นประเภทของพาเรนต์ ในทางปฏิบัติ วัตถุพาเรนต์อาจไม่ครบถ้วน (ซึ่งมีเหตุผลเพราะถ้าคุณกำลังแก้ไขฟิลด์ที่ยังไม่ได้ถูกแก้ไข วัตถุพาเรนต์จะยังไม่มีค่าในฟิลด์นั้น) วัตถุพาเรนต์ยังอาจมี ฟิลด์เพิ่มเติม ที่ไม่ได้ถูกกำหนดในประเภทของมัน ซึ่งทำให้สามารถส่งข้อมูลเพิ่มเติมไปยังตัวแก้ไขฟิลด์ได้ง่าย
