有没有编写多个条件函数签名的简便方法?

时间:2018-09-21 13:59:30

标签: typescript typescript2.0

我具有第一个参数确定第二个参数的功能。 它的行为类似于Foo

type stringF = (type: 'str', value: string) => void
type numberF = (type: 'num', value: number) => void
type booleanF = (type: 'bool', value: boolean) => void
...
... 

declare const Foo: stringF & numberF & booleanF //& etc..

共有6种功能类型。这是痛苦的,但可以控制。但是,现在有一个额外的参数作为第一个参数,用于指定是否应为数组。

所以变成了:

type stringF = (arr: false, type: 'str', value: string) => void
type numberF = (arr, false, type: 'num', value: number) => void
type booleanF = (arr, false, type: 'bool', value: boolean) => void
...

type stringF = (arr: true, type: 'str', value: string[]) => void
type numberF = (arr, true, type: 'num', value: number[]) => void
type booleanF = (arr, true, type: 'bool', value: boolean[]) => void
...

现在有12种功能类型。正确键入函数似乎并不值得麻烦

有没有更简单的方法来创建条件函数签名?

2 个答案:

答案 0 :(得分:1)

您可以使用条件类型为所有可能性创建单个签名:

type StringToType = { 
  str: string,
  num: number,
  bool: boolean
}
type MakeArrayIfTrue<TCondition, T> = TCondition extends true ? T[] :T;

type fn =  <MakeArray extends boolean, TypeKey extends keyof StringToType>(arr:  MakeArray, type: TypeKey, value: MakeArrayIfTrue<MakeArray, StringToType[TypeKey]>) => void

declare let fn : fn;
fn(true, "str", [""]);
fn(true, "bool", [""]); // error
fn(false, "str", [""]); // error

如果参数是联合,则其行为与多次重载有些不同。例如,这是有效的:

declare let b: boolean;
declare let strOrBool: "str" | "bool";
// Last parameter ca be boolean | string | boolean[] | string[]
fn(b, strOrBool, "") //ok
fn(b, strOrBool, [""]) //ok
fn(b, strOrBool, true) //ok
fn(b, strOrBool, 1) //this is still an error 

如果您想限制这种行为,我们可以变得更有创意,首先创建所有可能签名的并集,然后使用UnionToIntersection返回行为类似于带有重载功能的类型:

type StringToType = { 
  str: string,
  num: number,
  bool: boolean
}
type MakeArrayIfTrue<TCondition, T> = TCondition extends true ? T[] :T;

type UnionToIntersection<U> =  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
type FnHelper<MakeArray extends boolean = boolean, TypeKey extends keyof StringToType = keyof StringToType> = 
    MakeArray extends any ? TypeKey extends any ? (arr:  MakeArray, type: TypeKey, value: MakeArrayIfTrue<MakeArray, StringToType[TypeKey]>) => void: never: never;
type fn = UnionToIntersection<FnHelper>;

declare let fn : fn;
fn(true, "str", [""]);
fn(true, "bool", [""]); // error
fn(false, "str", [""]); // error

declare let b: boolean;
declare let strOrBool: "str" | "bool";
fn(b, strOrBool, "") //error
fn(b, strOrBool, [""]) //error
fn(b, strOrBool, true) //error

答案 1 :(得分:0)

在这种情况下,您应该使用Generic。因此,您的功能签名可能如下所示:

function myFunc<T>(value: T|T[]): void

它可以是数组,也可以不是数组,您只需要在函数中检查它即可。类型T代表所有不同的值类型。