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

typescript - มีวิธีใดบ้างที่จะ "กระจาย" คุณสมบัติของวัตถุไปยังพารามิเตอร์ของการนิยามฟังก์ชัน?


อธิบายได้ดีที่สุดด้วยโค้ด:

const ROUTE_PARAMS = {
  userId: 'userId',
  companyId: 'companyId',
};

// Is this somehow possible?
// `typeof` is obviously not the right tool here, just used as an example.
function buildRoute(typeof ROUTE_PARAMS): string;

// Result should be similar to:
function buildRoute2(userId: string, companyId: string): string;

// 🟩 The function should be called like this:
buildRoute('some-user-id', 'some-company-id');

// 🟥 I don't want the function to accept only 1 object like this:
buildRoute({ userId: 'some-user-id', companyId: 'some-company-id' });

แนวคิดคือการมีพารามิเตอร์เส้นทางของ buildRoute ถูก “ผูก” กับอ็อบเจ็กต์ในลักษณะที่แบบไดนามิก เมื่อกำหนดพารามิเตอร์ฟังก์ชัน “ด้วยตนเอง” (เช่นใน buildRoute2) ฉันมักต้องคอยให้มันสอดคล้องกันทุกครั้งเมื่อต้องเปลี่ยนคุณสมบัติของ ROUTE_PARAMS...


ข้อมูลเพิ่มเติม

ฉันจะพยายามเพิ่มข้อมูลบางอย่างเนื่องจากคำถามของฉันด้านบนดูเหมือนไม่ชัดเจนเพียงพอ:

  • ฉันกำลังมองหาวิธีอย่างชัดเจนในการ "พิมพ์" ฟังก์ชันนี้จาก วัตถุ เช่นเดียวกับที่แสดงด้านบน (ROUTE_PARAMS)
  • ฉันไม่สามารถเปลี่ยนสิ่งนี้เป็นโครงสร้างอื่น (ประเภททูเพิล, อาร์เรย์ชนิดใดชนิดหนึ่ง, ...) เพราะนั่นจะต้องมีการปรับโค้ดส่วนอื่น ๆ ที่ทำงานกับอ็อบเจ็กต์ ROUTE_PARAMS
  • ฟังก์ชันควรรับคุณสมบัติแต่ละอย่างของวัตถุเป็นพารามิเตอร์อินพุตเดียว (เช่น จำนวนพารามิเตอร์ของฟังก์ชัน === จำนวนคุณสมบัติของวัตถุ)
    ฉัน ไม่ ต้องการให้ฟังก์ชันรับคุณสมบัติทั้งหมดที่ถูกบรรจุรวมในวัตถุเดียว (ตัวอย่าง 🟩 & 🟥 ข้างต้น)

คำตอบ #1

ตอนแรก ฉันคิดว่าสิ่งที่คุณต้องการจะเป็นไปได้ง่ายโดยการใช้ตัวกระจาย (spread operator) กับชนิดทูเพิล (tuple type) ตัวอย่างเช่น,

function f(...args: [string, string]) {}

เหมือนกัน (ยกเว้นชื่อพารามิเตอร์) กับ

function f(arg0: string, arg1: string) {}

ดังนั้นเราจึงสามารถลอง:

const ROUTE_PARAMS = {
  userId: 'userId',
  companyId: 'companyId',
};

function buildRoute(...args: typeof ROUTE_PARAMS[keyof typeof ROUTE_PARAMS][]): void {}

อย่างไรก็ตาม, typeof ROUTE_PARAMS[keyof typeof ROUTE_PARAMS][] เป็นอาเรย์ ดังนั้นเราจึงสามารถส่งอาร์กิวเมนต์ได้ไม่จำกัดจำนวนไปยังฟังก์ชัน buildRoute ที่ได้ ซึ่งจะเหมือนกับเมื่อคุณเขียน function buildRoute(...args: string[]) นอกจากนี้ เมื่อค่าที่ใช้ไม่ใช่เพียง string เท่านั้น แต่รวมถึง เช่น number ประเภทที่อนุมานได้จะเป็น (string | number)[] และเราสามารถส่ง number ในตำแหน่งใดก็ได้

ความแตกต่างกับ f ข้างต้นคือ ประเภทของตัวแปรที่กระจายออกมาคือ tuple สำหรับ f แต่เป็น array สำหรับ buildRoute ดังนั้นเราจำเป็นต้องได้ tuple จากค่าของ ROUTE_PARAMS แต่โชคร้ายที่สิ่งนั้นไม่รองรับและจะไม่รองรับ เนื่องจากข้อเสนอที่สอดคล้องกันถูกปฏิเสธ: https://github.com/microsoft/TypeScript/issues/13298

ดังนั้นตอนนี้ฉันคิดว่าสิ่งที่คุณต้องการอาจจะเป็นไปไม่ได้กับ TypeScript

คำตอบ #2

คำตอบที่อัปเดต:

ดังที่ได้กล่าวไว้ในคำตอบของ Remirror การกระจายอาร์กิวเมนต์ฟังก์ชันไปยังชุดคุณสมบัติและประเภทเฉพาะบนตัวแปรไม่ได้รับการสนับสนุน คำตอบของ Remirror อธิบายได้ดีว่าทำไมถึงเป็นเช่นนี้

อย่างไรก็ตาม เพื่อต่อยอดคำตอบของ Remirror ให้ไวยากรณ์ดูสะอาดขึ้น คุณสามารถสร้างชนิดแบบ global ที่ใช้ generic สิ่งนี้สามารถนำกลับมาใช้ซ้ำได้ในทุกสถานที่ที่คุณต้องการโดยไม่ต้องเขียนโค้ดยาว ๆ ของ keyof ซ้ำไปซ้ำมา:

// Re-useable type
export type TypeFromVar<T> = T[keyof T][];


const ROUTE_PARAMS = {
  userId: 'userId',
  companyId: 'companyId',
};

const User = {
  name: 'Reece',
  age: 25,
  isVerified: true,
}


function buildRoute(...routes: TypeFromVar<typeof ROUTE_PARAMS>) {
  const [userId, companyId] = args;
  // do something
}

function otherFunction(...person: TypeFromVar<typeof User>) {
  const [name, age, isVerified] = args;
  // do something
}


buildRoute("userId1", "companyId2"); // only accepts string parameters

otherFunction("John", 36, false); // accepts string, number and boolean parameters

คำตอบต้นฉบับ:

นอกจากนี้ หากคุณสามารถกำหนดประเภทอาร์เรย์สำหรับตัวแปรของคุณ คุณสามารถรับจำนวนอาร์กิวเมนต์ที่แน่นอนและประเภทของมันได้โดยทำแบบนี้:

type RouteParams = [
  userId: string,
  companyId: string,
]

function buildRoute(...routes: RouteParams): string {
  const [userId, companyId] = routes;
  // do something
}


buildRoute("user1", "company2")

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