我想创建需要可选参数之一的函数类型。
据我所知,是要创建条件类型,但问题是函数打字稿中无法根据此条件缩小类型
type a = { propA: number };
type b = { propB: string };
type OneOfIsRequired = <T extends a | undefined, S extends b | undefined>
(parameterOne: S extends undefined ? a : T, parameterTwo?: T extends undefined ? b : S, x: number) => any;
const fn: OneOfIsRequired = (a, b) => {
if (a) {
const propA = a.propA;
} else {
const propB = b.propB; // Object is possibly 'undefined'.. typescript can not narrow type based on first if statement
}
};
fn(undefined, undefined, 1); // Argument of type 'undefined' is not assignable to parameter of type 'a' OK !, one of parameter is required
fn({ propA: 1 }, undefined, 1);
fn(undefined, { propB: '1' }, 1);
所以我希望在我的函数打字稿中,在其他情况下,可以缩小正确的类型,即“ b”而不是“ b | undefined”
任何想法我如何实现这种行为?我不想自己重新输入
答案 0 :(得分:1)
我认为条件类型不会对您有太大帮助。我可能会改用rest tuples的并集来描述可能的参数:
type OneOfIsRequired = (...args: [a, b | undefined, number] | [undefined, b, number]) => any;
调用它应该会给您相同的结果
fn(undefined, undefined, 1); // error
fn({ propA: 1 }, undefined, 1); // okay
fn(undefined, { propB: '1' }, 1); // okay
但是,这样做的好处是,与将通用条件类型缩小到其具体值之一相比,编译器更有可能将并集缩小到其要素之一。
尽管如此,实现仍然会抱怨,因为TypeScript类型保护只会缩小 single 值的类型。也就是说,在if (a) { } else { }
中,可能会在then和else子句中缩小a
的类型,但在选中{{1 }},即使b
和a
的类型之间存在一些约束。
自动进行类型保护的唯一方法是使a
和b
属于单个值,并检查该单个值。您可以制作自己的对象,例如
a
但是,如果在实现中使用其余的元组,则您已经有了这样的对象:
b
所以这可行,但是重构可能比您想做的要多。
如果这一切对您来说都太麻烦了,只需承认您比编译器更聪明,并使用type assertion来告诉您。具体来说,您可以通过const fn: OneOfIsRequired = (a, b, x) => {
const obj = { a: a, b: b } as { a: a, b: b | undefined } | { a: undefined, b: b };
if (obj.a) {
const propA = obj.a.propA;
} else {
const propB = obj.b.propB;
}
};
使用a non-null assertion:
// use the arguments as a tuple
const fn: OneOfIsRequired = (...ab) => {
if (ab[0]) {
const propA = ab[0].propA;
} else {
const propB = ab[1].propB;
}
};
!
只是意味着您已经告诉编译器const fn: OneOfIsRequired = (a, b) => {
if (a) {
const propA = a.propA;
} else {
const propB = b!.propB; // I am smarter than the compiler
}
};
不是b!
,无论它怎么想。错误消失了。与上面的解决方案相比,这种类型的安全性较差,但更简单,并且不会更改您发出的JavaScript。
好的,希望能有所帮助;祝你好运!