【问题标题】:Narrow type based on conditional type(Typescript)基于条件类型的窄类型(Typescript)
【发布时间】:2019-03-20 15:23:42
【问题描述】:

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

据我所知是制作条件类型,但问题是在函数打字稿中不能根据这个条件缩小类型

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);

所以我希望在 lse 条件下,我的函数 typescript 可以缩小正确的类型,即类型“b”而不是“b | undefined”

知道如何实现这种行为吗?我不想自己重新输入它

【问题讨论】:

    标签: typescript


    【解决方案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 { }中,有可能a的类型在then和else子句中会变窄,但是b的类型在检查a时不会变窄,即使有一些ab 的类型之间的约束。

    自动发生类型保护的唯一方法是让ab 成为单个值的一部分并检查该单个值。您可以制作自己的对象,例如

    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; 
      }
    };
    

    但是如果您在实现中使用其余元组,您已经拥有类似这样的对象:

    // use the arguments as a tuple
    const fn: OneOfIsRequired = (...ab) => {
      if (ab[0]) {
        const propA = ab[0].propA;
      } else {
        const propB = ab[1].propB; 
      }
    };
    

    所以这可行,但可能比您想要的重构更多。


    如果所有这些对你来说工作量太大,那就承认你比编译器更聪明,并使用type assertion 来告诉它。具体来说,你可以使用a non-null assertion 使用!

    const fn: OneOfIsRequired = (a, b) => {
      if (a) {
        const propA = a.propA;
      } else {
        const propB = b!.propB; // I am smarter than the compiler ?
      }
    };
    

    b! 只是意味着你告诉编译器b 不是undefined,不管它怎么想。并且错误消失了。这比上述解决方案的类型安全性差,但它更简单,并且不会更改您发出的 JavaScript。


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

    【讨论】:

    • 感谢您的努力,现在我做了这种愚蠢的事情“我比编译器更聪明”(我真的不喜欢这种代码)。但我会使用这种联合类型。简单的智能解决方案。我总是把打字稿的事情复杂化。非常感谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-26
    • 1970-01-01
    • 2017-11-01
    相关资源
    最近更新 更多