基于条件类型的窄类型(Typescript)

时间:2019-03-20 15:23:43

标签: typescript

我想创建需要可选参数之一的函数类型。

据我所知,是要创建条件类型,但问题是函数打字稿中无法根据此条件缩小类型

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”

任何想法我如何实现这种行为?我不想自己重新输入

1 个答案:

答案 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 }},即使ba的类型之间存在一些约束。

自动进行类型保护的唯一方法是使ab属于单个值,并检查该单个值。您可以制作自己的对象,例如

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。


好的,希望能有所帮助;祝你好运!