Typescript สามารถประกาศชนิดทั่วไปสำหรับอาเรย์ของสตริงจำนวน 1-N ได้หรือไม่
ค่อนข้างยากที่จะอธิบายในชื่อเรื่อง แต่โดยพื้นฐานแล้วฉันอยากจะสามารถประกาศอาร์เรย์ชนิดสตริงที่มีความยาวคงที่ตั้งแต่ 1 ถึง N
interface Command {
[key: string]: (args: [string, ...string[]]) => boolean;
}
const cmd: Command = {
TEST: (args: [string, string]) => args[0] === args[1],
TEST2: (args: [string]) => args[0] === 'hello'
}
ดังนั้นสิ่งนี้จึงใช้ไม่ได้ เนื่องจาก [string, string] แตกต่างจาก string[]:
Type '(args: [string]) => boolean' is not assignable to type '(args: [string, ...string[]]) => boolean'.วิธีแก้ปัญหาอาจเป็นการกำหนดเหมือนกับทุกประเภทของอาร์กิวเมนต์:
interface Command {
[key: string]: (args: [string] | [string, string] | [string, string, string]) => boolean;
}
แต่มันยาวไปหน่อยสำหรับสิ่งที่เรียบง่าย (เห็นด้วยว่ามันสามารถห่อหุ้มในอินเทอร์เฟซได้) อย่างไรก็ตามมีวิธีแก้ปัญหาที่สวยงามอีกวิธีหนึ่งที่ฉันไม่เห็นหรือเปล่า?
ขอบคุณ,
คำตอบ #1
ฉันสงสัยว่าคุณอาจต้องการให้ Command เป็นแบบ generic เพื่อให้คุณสมบัติของมันสามารถใช้งานได้จริง ถ้าคุณมีชนิดฟังก์ชันแบบ (arg: [string] | [string, string]) => boolean คุณจะไม่สามารถใช้งานมันด้วย (arg: [string]) => boolean หรือ (arg: [string, string]) => boolean ได้ ชนิดของฟังก์ชันจะเปลี่ยนแปลงอย่าง contravariant ในชนิดของอาร์กิวเมนต์ คุณสามารถขยายได้ แต่ไม่สามารถทำให้แคบลงในชนิดของอาร์กิวเมนต์ฟังก์ชันได้ เว้นแต่คุณต้องการให้ทุกวิธีของชนิด Command ต้องรับอาร์เรย์ของสตริงทุกประเภทที่มีความยาวหนึ่งหรือมากกว่า คุณจะต้องทำบางอย่างกับ generics
นี่คือตัวอย่างชนิดที่เป็นไปได้สำหรับ Command พร้อมกับฟังก์ชันช่วยเหลือ asCommand() ซึ่งช่วยให้คอมไพเลอร์สามารถสรุปค่าที่ถูกต้องของ T จากค่าชนิด Command<T> โดยไม่ต้องบังคับให้คุณเขียนมันด้วยตัวเอง:
type Command<T> = { [K in keyof T]: (args: T[K]) => boolean }
const asCommand = <T extends Record<keyof T, [string, ...string[]]>>(
cmd: Command<T>
) => cmd;
จากนั้น ค่าคงที่ cmd ของคุณสามารถประกาศได้แบบนี้โดยไม่มีข้อผิดพลาด:
const cmd = asCommand({
TEST: (args: [string, string]) => args[0] === args[1],
TEST2: (args: [string]) => args[0] === 'hello'
});
และคอมไพเลอร์จะจำได้ว่า cmd.TEST รับคู่ค่าและ cmd.TEST2 รับทูเพิลหนึ่งค่า:
cmd.TEST(["", ""]); // okay
cmd.TEST([]); // error
cmd.TEST([""]); // error
cmd.TEST(["", "", ""]); // error
cmd.TEST2([""]); // okay
cmd.TEST2([]); // error
cmd.TEST2(["", ""]); // error
cmd.TEST2(["", "", ""]); // error
และคุณไม่สามารถให้สมบัติที่รับเฉพาะทูเพิลความยาวศูนย์ได้:
const badCmd = asCommand({
OOPS: (args: []) => false, // error!
})
ฉันหวังว่านั่นจะช่วยให้คุณมีทิศทางบ้าง โชคดีนะ!
ลิงก์ Playground ไปยังโค้ด
