我有一个带有三个参数的类方法声明:
declare class Application {
PrintOut(OutputFileName?: string, PrintToFile?: boolean, StartPage?: number): void;
}
打印到文件时,必须传递输出文件名 ;当不打印到文件时,无法传递文件名 。这意味着OutputFileName
参数的类型取决于PrintToFile
参数,如下所示:
PrintToFile
为true
,则OutputFileName
的类型为string
而不是undefined
,即不是可选的。PrintToFile
是false
/ undefined
,或者未通过),则OutputFileName
必须是undefined
或者,使用重载:
declare class Application {
PrintOut(OutputFileName: string, PrintToFile: true, StartPage?: number): void;
PrintOut(OutputFileName?: undefined, PrintToFile?: false, StartPage?: number): void;
}
以下呼叫有效:
x.PrintOut('abcd', true);
x.PrintOut(undefined, false);
x.PrintOut(undefined, undefined);
x.PrintOut();
,以下调用无效:
x.PrintOut(undefined, true); // first parameter must be a string
x.PrintOut('abcd', false); // cannot pass a filename if not printing to a file
x.PrintOut('abcd', undefined); // (same as above)
x.PrintOut('abcd'); // (same as above)
如果只有两个参数,我会坚持使用重载。但这只是MCVE。 actual method具有19个参数,其中多个参数取决于3个不同参数中每个参数的特定值;并将类似的方法应用于5种不同的对象类型。
使用此:
type IfPrintToFile<T, U> =
T extends true ? U :
undefined;
declare class Application {
PrintOut<T extends boolean>(OutputFileName?: IfPrintToFile<T, string>, PrintToFile?: T, StartPage?: number): void;
}
不能阻止这些情况:
x.PrintOut(undefined, true);
x.PrintOut('abcd', undefined);
x.PrintOut('abcd');
在将方法的约束定义为T extends boolean | undefined
时仍然不会阻止这些约束:
x.PrintOut(undefined, true);
x.PrintOut('abcd');
根据需要定义参数:
type IfPrintToFile<T, U> =
T extends true ? U :
undefined;
declare class Application {
PrintOut<T extends boolean | undefined>(OutputFileName: IfPrintToFile<T, string>, PrintToFile: T, StartPage?: number): void;
}
阻止所有无效的呼叫,但也阻止以下有效的呼叫:
x.PrintOut();
1。看来选项设置没有存储在Playground URL中,并且每次都需要手动设置。
答案 0 :(得分:2)
您现在可以使用rest参数来执行此操作(自jcalz在2018年回答以来,情况似乎有所改善!):
declare class Application {
PrintOut<T extends boolean | undefined>(
...args: T extends true ? [
OutputFileName: string,
PrintToFile: T,
StartPage?: number,
] : [
OutputFileName?: undefined,
PrintToFile?: T,
StartPage?: number,
]
): void;
}
const x = new Application();
// valid calls
x.PrintOut('abcd', true);
x.PrintOut(undefined, false);
x.PrintOut(undefined, undefined);
x.PrintOut();
// invalid calls
x.PrintOut(undefined, true);
x.PrintOut('abcd', false);
x.PrintOut('abcd', undefined);
x.PrintOut('abcd');
这将精确地产生预期的错误并允许所有有效形式。参见playground。
答案 1 :(得分:1)
我认为这可能无法完成。
据我所知,不能在函数参数(或对象属性)的类型内以编程方式打开或关闭可选函数参数(和对象属性)。也就是说,无法创建使它们等效的Optional<>
类型别名
type FuncA = (x?: string) => void;
type FuncB = (x: Optional<string>) => void;
或这些等价物
type ObjA = {x?: string};
type ObjB = {x: Optional<string>};
有一个open issue,但我认为没有任何进展。因此,您想做的事可能就在那里受阻。
的确,从TS3.0开始,您将能够use tuple types in rest/spread positions,并且有一种方法可以处理一些可选参数:
元组类型中的可选元素
元组类型现在允许在元素类型上使用
?
后缀以指示该元素是可选的:let t: [number, string?, boolean?]; t = [42, "hello", true]; t = [42, "hello"]; t = [42];
元组类型允许元素省略,只要该元素的类型具有后缀
?
,并且其右侧的所有元素也具有?
修饰符。当为其余参数推断出元组类型时,源中的可选参数将成为推断出的类型中的可选元组元素。
因此您可能很想使用它来创建可能的参数元组的联合,如下所示:
type PrintOutParams = [string, true, number?] | [undefined?, false?, number?]
但是您将不能在rest参数中使用它:
PrintOut(...args: PrintOutParams): void;
// error: A rest parameter must be of an array type.
因为a union of array types is not considered to be an array type itself类型检查其余参数。至少从2015年起,此问题被认为是“设计使然”,不需要它的原因是……嗯,see for yourself:
我们也许可以将规则从“ rest参数必须是数组类型”更改为“ rest参数必须是其中所有成分都是数组类型的数组类型或联合类型”,但这似乎不值得为之增加复杂性,因为添加重载来阐明预期的行为很容易。
这是真的……过载确实会为您提供所需的行为。您是否考虑过使用重载?
哦,对。
抱歉,我没有更好的消息。希望无论如何都会有所帮助;祝你好运!