วิธีใช้การโอเวอร์โหลดฟังก์ชันของ TypeScript กับฟังก์ชันที่มีหลายพารามิเตอร์?
ฉันมีฟังก์ชันที่พยายามหาค่าหนึ่งในอาเรย์ของตัวเลข ค่านั้นสามารถเป็นได้ทั้งออบเจ็กต์หรือเลข หากค่านั้นเป็นออบเจ็กต์ จะมีคุณสมบัติ 'key' ที่ใช้เพื่อดึงค่าตัวเลขจากออบเจ็กต์นั้น
ฉันกำลังพยายามใช้ การโอเวอร์โหลดฟังก์ชัน เพื่อให้มีฟังก์ชันเดียวที่สามารถจัดการกับทั้งสองกรณีได้
type ObjectType = {
[key: string]: number
}
type FunctionType = {
<T extends ObjectType>(v: T, list: number[], key: string): T | undefined
(v: number, list: number[]): number | undefined
}
const find: FunctionType = <T extends ObjectType | number,>(v: T, list: number[], key?: string)=>{
const value = typeof v === 'number' ? v : v[key]
return list.find((item)=>{
return item === value
})
}
นี่สร้างข้อผิดพลาด Type undefined ไม่สามารถใช้เป็นประเภทดัชนีได้
แต่ในสาขานี้ ค่า v เป็นวัตถุ ดังนั้นทำไม key ถึงไม่ได้ถูกกำหนด?
สนามเด็กเล่น TS
ขอบคุณ
แก้ไข
มีการเสนอวิธีแก้ปัญหาโดยอเล็กซ์ เวย์น แต่ไม่ได้จำกัดค่าให้เป็นตัวเลขอย่างเหมาะสม
ถ้าแทนที่จะเรียก list.find เราเรียกฟังก์ชันที่กำหนดเองซึ่งรับเฉพาะตัวเลขเท่านั้น TypeScript จะเกิดข้อผิดพลาด
const findNumber = (v: number, list: number[])=>{
/* Do stuff with v as a number */
return list.indexOf(v)
}
const find: FunctionType = <T extends ObjectType | number>(v: T, list: number[], key?: keyof T)=>{
const value = (key && typeof v === 'object') ? v[key] : v
return list[findNumber(value, list)]
}
ประเภท 'ObjectType' ไม่สามารถกำหนดค่าให้กับประเภท 'number' ได้
สนามเด็กเล่น TS
คำตอบ #1
ฟังก์ชันการใช้งานไม่มีความรู้เกี่ยวกับฟังก์ชันที่มีการโอเวอร์โหลดของมัน ซึ่งหมายความว่าการใช้งานฟังก์ชันโอเวอร์โหลดจะต้องยังคงถูกต้องสมบูรณ์แม้ว่าลายเซ็นของฟังก์ชันโอเวอร์โหลดจะถูกลบออก
ดังนั้นแม้ว่าจะเป็นความจริงว่าถ้า typeof v === 'number' แล้ว typeof key === 'string' ตัวคอมไพเลอร์ก็ไม่ทราบเรื่องนั้น
ถ้าคุณดูเพียงแค่ลายเซ็นของฟังก์ชันนี้:
<T extends ObjectType | number,>(v: T, list: number[], key?: string)=>{
จากนั้นคุณจะเห็นว่า find({ someKey: 1 }, [1,2,3]) เป็นการเรียกที่ถูกต้อง การโอเวอร์โหลดจะป้องกันการเรียกนั้น แต่ฟังก์ชันนี้ต้องถูกต้องแม้ไม่มีการโอเวอร์โหลดเหล่านั้น
ซึ่งหมายความว่าในการแก้ไขนี้ คุณจะต้องตรวจสอบการมีอยู่ของ key ก่อนใช้งาน
const value = (key && typeof v === 'number') ? v : v[key]
อย่างไรก็ตาม นี่ทำให้เกิดปัญหาใหม่:
Type 'undefined' cannot be used as an index type.(2538)
ฉันเชื่อว่าสาเหตุนี้เป็นเพราะ typeof v === 'number' ไม่ใช่การปรับปรุงที่เพียงพอ ฉันไม่แน่ใจว่ามันคิดว่ามันอาจเป็นอะไรอย่างอื่นบ้าง แต่ถ้าคุณกลับค่า ternary และปรับปรุงโดยใช้ typeof v === 'object' แทน TypeScript ก็จะพอใจ
const value = (key && typeof v === 'object') ? v[key] : v
สนามเด็กเล่น
สุดท้าย คุณอาจปรับปรุงความปลอดภัยของประเภทที่นี่ได้โดยใช้ key: keyof T แทน
const find: FunctionType = <T extends ObjectType | number>(v: T, list: number[], key?: keyof T)=>{
const value = (key && typeof v === 'object') ? v[key] : v
return list.find((item)=>{
return item === value
})
}
ดูสนามเด็กเล่น
คำตอบ #2
ก่อนอื่น มาทำให้แน่ใจว่าสถานะที่ไม่ถูกต้องไม่สามารถแทนค่าได้ มานิยามชนิดยูเนียนกัน:
type IsObj<T> = {
value: T, key: keyof T
}
type IsNumber = { value: number }
type Params<T> = IsObj<T> | IsNumber
อย่างที่คุณอาจสังเกตเห็น ฉันได้ผูกอาร์กิวเมนต์ที่ 2 และ 3 แล้ว
ตอนนี้ เราสามารถกำหนด typeguard ที่กำหนดเองของเราได้:
const isNumber = <T,>(params: Params<T>): params is IsNumber => typeof params.value === 'number'
มารวมทุกอย่างเข้าด้วยกันเถอะ:
type ObjectType = {
[key: string]: number
}
type IsObj<T> = {
value: T, key: keyof T
}
type IsNumber = { value: number }
type Params<T> = IsObj<T> | IsNumber
const isNumber = <T,>(params: Params<T>): params is IsNumber => typeof params.value === 'number'
type FunctionType = {
<T extends ObjectType>(list: number[], params: IsObj<T>): T | undefined
(list: number[], params: IsNumber): number | undefined
}
const find: FunctionType = <T extends ObjectType | number,>(list: number[], params: Params<T>) => {
const value = isNumber(params) ? params.value : params.value[params.key]
return list.find((item) => item === value)
}
const result = find([1, 2, 3], { value: 42}) // ok
const result_ = find([1, 2, 3], { value: { age: 2 }, key: 'age' }) // ok
const result__ = find([1, 2, 3], { value: { age: 2 }, key: 'name' }) // expected error
const result___ = find([1, 2, 3], { value: 42, key: 'name' }) // expected error
สนามเด็กเล่น
