【问题标题】:Typescript Generics: Use output argument type to build input argument type打字稿泛型:使用输出参数类型来构建输入参数类型
【发布时间】:2020-03-07 21:35:09
【问题描述】:

我想使用泛型来减少维护,以及维护 Typescript 类型时可能出现的错误。

我有一个名为loadValues 的函数,它接收具有以下示例接口的对象:

interface DashboardPreload: {
    studentNames: Observable<string[]>,
    seatNumbers: Observable<number[]>
}

并返回一个具有以下非常相似接口的对象。请注意,它具有相同的键,并且键具有相同的值,除了在 DashboardPreload 中,它被包装在一个 Observable 中:

interface DashboardState: {
    studentNames: string[],
    seatNumbers: number[] 
}

所以指定类型如下所示:

function loadValues(preload: DashboardPreload): DashboardState {
    // ...
}

为了跨组件重用loadValue 函数,我更改了签名以允许泛型:

loadValues<A, B>(preload: A): B {
    // ...
}
state = loadValues<DashboardState, DashboardPreload>(toPreload);
// another component would then do
interface UserDetailPreload: {
    name: Observable<string>;
    address: Observable<string>;
}
interface UserDetailState: {
    name: string;
    address: string;
}
otherState = loadValues<UserDetailState, UserDetailPreload>(otherValuesToPreload);

但我仍然想赢得一场巨大的胜利。我的输入和输出对象

  1. 总是有相同的键
  2. 和对应的键值类型是相同的,除了在输入对象上,值被包装在一个 Observable 中。

为了避免必须维护非常相似的类型,如果泛型可以指定输入和输出类型之间的上述关系,那就太好了。我想简单地提供一个通用参数(例如输出),并且签名的编写方式可以键入输入和输出:

// How should this signature look?
function loadValues<P>(preload: ?): P {
    // ...
}
state = loadValues<DashboardState>(toPreload);

这可能吗?函数签名的外观应该如何(请注意,我不是在询问函数中的逻辑,而是询问如何进行泛型)。

  1. 诸如 Object.entries() 之类的 Typescript 签名,
  2. 类似“K extends keyof P”的结构
  3. 例如:https://medium.com/iqoqo-engineering/two-advanced-techniques-to-make-you-a-typescript-wizard-df42e00b1cf8

但还没有找到解决办法。

【问题讨论】:

    标签: javascript angular typescript generics types


    【解决方案1】:

    您可以像这样添加 Preload 类型:

    type Preload<T> = {
      [P in keyof T]: Observable<T[P]>;
    };
    function loadValues<P>(preload: Preload<P>): P {
      // ...
    }
    state = loadValues<DashboardState>(toPreload);
    

    【讨论】:

    【解决方案2】:

    我不确定我是否理解您想要实现的目标,因此这可能无法给出完整的答案,但也许这可能会将您推向正确的方向。

    如果您也将preload 参数的类型设为通用,那么打字稿应该能够推断出通用返回类型。您需要为此引入一个新的通用接口。

    所以你的函数签名应该是这样的:

    function loadValues<P>(preload: PreloadState<P>): P {
        // ...
    }
    
    interface PreloadState<P> { }
    

    当你像这样使用它时

    var preload: PreloadState<DashboardState> = { .. };
    var result = loadValues(preload);
    

    result 的类型将被正确推断为DashboardState 类型。

    【讨论】:

    • 感谢@Nikolaj 我添加到问题中以表明我可以使用泛型实现这一中间步骤。我希望我只能在函数中指定输入类型并“构建”输出类型,因为输入对象和输出对象的外观之间存在固定的关系。几乎就像 Object.entries “构建”它的输出: interface Object { entries(o: { [key in X]: Y }): [X, Y][]; }
    猜你喜欢
    • 2018-05-02
    • 2019-07-05
    • 1970-01-01
    • 2020-03-01
    • 2020-12-22
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 2022-11-11
    相关资源
    最近更新 更多