typescript - 如何根据输入参数键入返回类型

标签 typescript typescript-generics

我有一个接收 options 的函数带有 kind 的参数属性。 kind 的可能值是一小组值。所以它基本上是一个枚举。

取决于 kind函数应该有不同的返回值。虽然所有可能的返回值都从一些常见的基本类型扩展而来。

我可以通过重载实现我想要的,但是函数本身的类型不是很好:

function test(options: Scenarios['bar']['options']): Scenarios['bar']['ret'];
function test(options: Scenarios['foo']['options']): Scenarios['foo']['ret'];
function test(options: any): any {
  ...
};

有没有一种使用泛型键入此内容的好方法?这将是完美的 if(options.kind === 'foo') { return ... }也会正确地强制执行正确的返回类型。

这就是我尝试过的,但它不起作用。
type Base {
  a: string;
}

type Foo {
  b: string;
}

type Bar {
  c: string;
}

interface Scenarios {
  foo: { options: { kind: 'foo', input: string }, ret: Foo },
  bar: { options: { kind: 'bar' }, ret: Bar },
}

function test<S extends keyof Scenarios, O extends Scenarios[S]['options'], R extends Scenarios[S]['ret']>(options: O): R {
  const out: Partial<R> = {
    a: 'one',
  };
  if(options.kind === 'foo') {
    out.b = options.input;
  }
  if(options.kind === 'bar') {
    out.c = "whatever"
  }

  return out;
}

这里没有O也不是 R似乎没有正确输入。我收到多个错误:
  • a: 'one',对象字面量错误可能仅指定已知属性,并且类型“部分”中不存在“a”
  • options.input类型“O”上不存在属性“输入”的错误。
  • out.b (和 out.c )类型“部分”上不存在属性“b”的错误。
  • 最佳答案

    我不是这方面的专家,但在根据您的要求玩了一段时间后,我意识到了一些事情:

  • TypeScript 只能区分联合类型;它不能区分泛型。
  • 所述歧视不会向上传播;也就是说,TypeScript 不会根据对子对象的区分来自动区分父对象。

  • 话虽如此,这是我想出的解决方案:
    type Base = { // I'm assuming this is the base type for Foo and Bar
        a: string;
    }
    
    interface Foo extends Base { // so I shall modify here a bit
        b: string;
    }
    
    interface Bar extends Base { // and here of course
        c: string;
    }
    
    interface Scenarios {
        // Now I put the return type inside the option, to make the discrimination work
        // Of course, I need to make 'ret' optional, or your input won't make sense
        foo: { kind: 'foo', input: string, ret?: Foo },
        bar: { kind: 'bar', ret?: Bar },
    }
    
    // Notice the use of 'Required' here; that brings the actual type of 'ret' back
    function test<X extends keyof Scenarios>(options: Scenarios[X]): Required<Scenarios[X]>['ret'] {
        let data = options as Scenarios[keyof Scenarios]; // create a union type
        data.ret = { a: 'one' } as Scenarios[keyof Scenarios]['ret']; // type assertion
    
        if (data.kind === 'foo') {
            data.ret!.b = data.input; // finally the discrimination works!
        }
        if (data.kind === 'bar') {
            data.ret!.c = "whatever"
        }
        return data.ret!;
    }
    

    好的,到目前为止一切顺利,不幸的是我仍然无法让 TypeScript 自动推断泛型参数。说如果我跑:
    var result = test({ kind: 'foo', input: "aas" }); // oops
    

    然后 TypeScript 仍然无法弄清楚 result类型为 Foo .但是,当然,这种自动推理的实际值(value)非常低,因为即使它有效,它也只有在您在函数的参数中逐字键入“foo”一词时才有效,并且如果您可以这样做,什么阻止你输入泛型参数?

    您可以在此 Playground Link 中尝试这些代码

    更新:

    我刚刚找到了我最后一个问题的解决方案:
    function test<X extends keyof Scenarios>(options: { kind: X } & Scenarios[X]): Required<Scenarios[X]>['ret'] {
        ....
    }
    
    var result = test({ kind: 'foo', input: "aas" }); // It works!
    

    诀窍是添加 { kind: X }到参数声明。

    看到这个 Playground Link .

    关于typescript - 如何根据输入参数键入返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61368548/

    相关文章:

    javascript - RxJs-Observable-如何从 JSON 对象获取数据

    javascript - typescript编译后是否可以保留原来的函数头

    javascript - 我如何使用 moment.js 添加天数,不包括周末?

    html - Angular 5 : Spinner is not working perfectly on page load

    typescript - 从 typescript 接口(interface)中提取嵌套字段并将其合并为新类型

    javascript - 防止 Router.Navigate 上的页面跳转

    typescript - 如何使用通用对象和键类型作为字典中的键

    typescript - noImplicitAny 不适用于通用高阶函数

    javascript - typescript 错误代码 : 2366, 函数缺少结束返回语句并且返回类型不包括 'undefined'