簡體   English   中英

如何在 TypeScript 中重載可選 boolean

[英]How to overload optional boolean in TypeScript

我正在嘗試推斷一些使用 combobox 組件的類型。

基本上,給定一個類型T ,選項應該是T[] ,並且 onChange 處理程序應該是(value: T) => void 但是,如果isMulti標志為真,那么 onChange 應該是(value: T[]) => void 我只是不確定如何配置重載類型以使其正常工作:

type Options<T = any> = {
    options: T[]
} & ({
    isMulti?: false
    onChange: (value: T) => void
} | {
    isMulti: true
    onChange: (value: T[]) => void
})

interface Option {
    value: string,
}

const a: Options<Option> = {
    // isMulti: false,
    options: [{
        value: 'abc',
    }],
    onChange: (value) => console.log(value)
}

TypeScript 游樂場

基本上問題是如果isMulti未定義,那么 onChange 中的值是any

參數“值”隱含地具有“任何”類型。

有什么辦法可以做到這一點,還是我需要使isMulti必需的?

事實證明,我們必須在帶有約束的onChange方法中添加額外的泛型:

type A<T> = {
    isMulti: false,
    onChange: (value: T) => any
}
type B<T> = {
    isMulti: true,
    onChange: (value: T[]) => any

}
type C<T> = {
    onChange: (value: T) => any
}

// credits goes to Titian Cernicova-Dragomir
//https://stackoverflow.com/questions/65805600/struggling-with-building-a-type-in-ts#answer-65805753
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
    T extends any
    ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;

type StrictUnion<T> = StrictUnionHelper<T, T>

type Unions<T> = StrictUnion<A<T> | B<T> | C<T>>

type Options<T = any> = {
    options: T[]
} & Unions<T>

interface Option {
    value: string,
}

const a: Options<Option> = {
    options: [{
        value: 'abc',
    }],

// trick is here
    onChange: <T extends Option>(value: T) => {
        return value
    }
}

const b: Options<Option> = {
    isMulti: true,
    options: [{
        value: 'abc',
    }],
    onChange: <T extends Option[]>(value: T) => {
        return value
    }
}
const c: Options<Option> = {
    isMulti: false,
    options: [{
        value: 'abc',
    }],
    onChange: <T extends Option>(value: T) => {
        return value
    }
}

// error because if isMulti is false, value should be Option and not an array of Option
const d: Options<Option> = {
    isMulti: false,
    options: [{
        value: 'abc',
    }],
    // should be T extends Option instead of T extends Option[]
    onChange: <T extends Option[]>(value: T) => {
        return value
    }
}

a.onChange({ value: 'hello' }) // ok
b.onChange([{ value: 'hello' }]) // ok
c.onChange({ value: 'hello' }) // ok
c.onChange([{ value: 'hello' }]) // expected error

如果你想了解這個技巧,請閱讀答案

我可能有一個解決方法,但我不知道這是否是最好的解決方案。 我為每個場景嘗試了顯式類型,並定義了一個重載的 function 來傳遞 object (盡管我希望這項工作沒有不切實際的功能):

interface Option {
    value: string,
}

// default Options
type Options<T = Option> = {
    options: T[]
}

// isMulti is undefined
type U<T> = {
    onChange: (value: T) => void
}

// isMulti is false
type F<T> = {
    isMulti: false
} & U<T>

// isMulti is true
type M<T> = {
    isMulti: true
    onChange: (value: T[]) => void
}

// overloads for determining given parameters
function getOptions<T>(options: Options<T> & M<T>): typeof options;
function getOptions<T>(options: Options<T> & U<T>): typeof options;
function getOptions<T>(options: Options<T> & F<T>): typeof options;
function getOptions(options: any) {
    return options;
}

const a = getOptions({
    // isMulti: false,
    options: [{
        value: 'abc',
    }],
    onChange: (value) => console.log(value)
})

這正如我所希望的那樣工作,但它似乎仍然有點矯枉過正,例如,我不確定這是否適用於 JSX/React 組件的 props。

查看 typescript 操場( 鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM