打字稿保持顺序或返回值的类型(如果是映射对象)

时间:2019-09-02 16:21:47

标签: typescript types iteration mapping

我正在研究at对象的简单映射器。它应该接受对象属性的数组,并返回这些属性的值的数组。 它工作正常,但是在Typescript的情况下,我无法保存返回值的类型顺序。

这里是例子

import { createStore, Store as FriendsStore } from "./mobx_react_lite_store";
import { createAccountsStore, AccountsStoreType } from "./accountsStoreLite";

type RootObj = {
    friendsStore: FriendsStore,
    accountsStore: AccountsStoreType
};

const rootStore: RootObj = {
    friendsStore: friendsStore,
    accountsStore: accountsStore
};

function pickStores<T, K extends keyof T>(rootStore: T, keys: K[]): T[K][] {
    return keys.map((key: K) => {
        const store = rootStore[key];

        if (!store) {
            throw new Error(`Can't find the store with name ${key} in rootStore. Myabe yo've forgottent
            to add this store to the rootStore.`);
        }

        return store;
    }) 
}

当我尝试仅在一家商店中称呼它时,它就像一个饰物 对于前

// Works fine. It shows that accountsStore has AccountsStoreType
const [ accountsStore ] = pickStores(rootStore, ["accountsStore"]);

// Works fine. It shows that accountsStore has friendsStore type
const [ friendsStore ] = pickStores(rootStore, ["friendsStore"]);

但是当我试图用2存储调用它时,返回的类型不保持某些顺序。 因此friendsStore可以是EITHER friendsStore或AccountsStoreType。 accountsStore

也是一样
const [ friendsStore, accountsStore ] = pickStores(rootStore, ["friendsStore", "accountsStore"]);

Here is the screenshot

是否可以为每个被破坏的财产保存正确的类型? 感谢您的帮助。

更新 lukasgeiter 提供了正确的答案,但是对于Typescript来说,有趣的技巧很少。我已经研究了它们,寻找了一些文档等,这是了解这些功能的有用资源:

希望此信息对某人有帮助。

About typle

Allow deriving from object and intersection types

About lookup types

About mapped types

1 个答案:

答案 0 :(得分:1)

要解决此问题,我们必须弄清楚两件事...

1。元组

要能够处理keys数组中元素的各个类型,我们需要TypeScript将其视为元组:

当前TypeScript将keys参数的类型推断为:

("friendsStore" | "accountsStore")[]

但是,我们想要的是这种类型:

["friendsStore", "accountsStore"]

要实现此目的,有几种解决方案:

铸造

最简单但也是最冗长的选择是将数组转换为元组:

<["friendsStore", "accountsStore"]>["friendsStore", "accountsStore"]

Const

或者,我们也可以将数组强制转换为const。这会将其转换为readonly元组:

<const>["friendsStore", "accountsStore"] // -> readonly ["friendsStore", "accountsStore"]

其他参数

如果我们将keys更改为rest参数,TypeScript将推断其类型为元组。继续阅读以查看实际效果。

2。映射类型

使用正确键入的元组,我们可以构造一个返回类型,该返回类型将键映射到T中的类型:

{ [I in keyof K]: T[keyof T & K[I]] }
  • [I in keyof K]遍历keys数组的元素。 I是索引,而不是元素本身。
  • K[I]获取元素的类型
  • T[keyof T & K[I]]K[I]上查找属性T的类型。我必须添加keyof T &才能满足编译器的要求。如果丢失,它将抱怨K[I]不是T的键,即使我们知道情况总是如此。


完整代码

我已经创建了两个版本的代码,它们应该适用于一个版本。一个使用rest参数,另一个使用const强制转换。

不幸的是,我不得不在函数内部引入any强制转换。可能可以将其转换为更具体的内容,但这是我所能找到的最好的结果。

带有休息参数

  

请注意由于rest参数而导致函数调用方式的变化

function pickStores<T, K extends (keyof T)[]>(rootStore: T, ...keys: K): { [I in keyof K]: T[keyof T & K[I]] } {
    return keys.map((key: keyof T) => {
        // ...
    })  as any;
}

const [ friendsStore, accountsStore ] = pickStores(rootStore, "friendsStore", "accountsStore");

使用Const Cast

function pickStores<T, K extends readonly (keyof T)[]>(rootStore: T, keys: K): { [I in keyof K]: T[keyof T & K[I]] } {
    return keys.map((key: keyof T) => {
        // ...
    })  as any;
}

const [ friendsStore, accountsStore ] = pickStores(rootStore, ["friendsStore", "accountsStore"] as const);