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

วิธีใช้การโอเวอร์โหลดฟังก์ชันของ 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

สนามเด็กเล่น