扩展号码但不限于特定号码

extends number but do not restrict to a specific number

可能有人问过并回答过,但我没有找到答案 - 所以我问了。

解决方法不是很好,因为如果我想包含更多类型,那么升级就不会那么容易。 有更好的方法吗?

declare const max: <A extends number | string> (a: A) => (b: A) => A

max (2) (3)     // Argument of type '3' is not assignable to parameter of type '2'
max ('a') ('b') // Argument of type '"b"' is not assignable to parameter of type '"a"'

// Possible workaround but will become clumsy if possible type to extends grow

declare const maxClumsy: {
    (a: number): (b: number) => number
    (a: string): (b: string) => string
}

maxClumsy (2) (3)
maxClumsy ('a') ('b')

playground link

跟进来自@matt-diamond

的评论

Keep in mind that you're passing in literals here... if the input variables are typed more broadly, you won't have an issue.

如果你不想每次都传递明确类型的变量,你可以使用这个方法。

type Num = number | string
 
type AsNum<T> = T extends string ? string : 
   T extends number ? number : never 

declare const max: <T extends Num>(a: T) => (b: AsNum<T>) => AsNum<T>

max(2,4)  max('2','4') // OK
max(2,'4') // Argument of type 'string' is not assignable to parameter of type 'number'
max(true,false) // Argument of type 'boolean' is not assignable to parameter of type 'string | number'

尽管它看起来确实和您的工作建议一样“笨拙”,甚至更多。

我说函数重载是解决问题的方法。对于两种类型,您的声明似乎没问题:

declare const maxClumsy: {
    (a: number): (b: number) => number
    (a: string): (b: string) => string
}

但是如果你真的关心 stringnumber 的重复,那么你可以通过编程构造这个类型;函数重载相当于函数类型的交集,因此我们可以使用一个distributive conditional type and the UnionToIntersection helper from 将联合类型string | number转换为所需的类型:

type CurriedUnion<T> = T extends any ? (a: T) => (b: T) => T : never
type UnionToIntersection<T> = (T extends any ? (a: T) => void : never) extends (a: infer U) => void ? U : never
type CurriedOverload<T> = UnionToIntersection<CurriedUnion<T>>

用法:

// type Test = ((a: string) => (b: string) => string) & ((a: number) => (b: number) => number)
type Test = CurriedOverload<string | number>

declare const coolMax: CurriedOverload<string | number>

// OK
coolMax (2) (3)
// OK
coolMax ('a') ('b')
// error
coolMax (2) ('b')
// error
coolMax ('a') (3)

Playground Link

请注意,当输入类型本身是联合时,像这样分布联合类型可能会导致意外结果;特别是,boolean 被定义为联合类型 true | false,因此在那种情况下这不会执行您想要的操作。