繁体   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