typescript 嵌套泛型中的索引访问问题

ddrv8njm  于 2023-02-25  发布在  TypeScript
关注(0)|答案(1)|浏览(170)

我有一个db-config文件,格式如下:

const simpleDbConfig: DbConfig = {
    firstTable: {
        columns: {
            foo: { nameDb: "Foo", dataType: "TEXT" },
            bar: { nameDb: "Bar", dataType: "TEXT" },
        },
    },
};

以及以下类型定义:

interface DbConfig {
    firstTable: DbTable<"foo" | "bar">;
}

interface DbTable<T extends string> {
    columns: ColumnObject<T>;
}

type ColumnObject<T extends string> = Record<T, Column>;

interface Column {
    nameDb: string;
    dataType: string;
}

现在我要做的是写一个函数,它获取一个表的键,一个表的列的键的数组,以及这些列的属性,它应该返回一个对象,将这些列的键Map到这些列的传入属性的值。
函数如下:

Version 1:

function getColKeyToPropMap<
    TDbTblJsName extends keyof DbConfig,
    TDbTblColObjRecord extends DbConfig[TDbTblJsName]["columns"],
    TDColJsName extends keyof TDbTblColObjRecord,
    TDbColObj extends TDbTblColObjRecord[TDColJsName],
    TColPropName extends keyof TDbColObj
>(tbl: TDbTblJsName, cols: TDColJsName[], colPropName: TColPropName) {
    const tblInfo: TDbTblColObjRecord = simpleDbConfig[tbl].columns as TDbTblColObjRecord;
    const resObj: Record<TDColJsName, TDbColObj[TColPropName]> = {} as Record<TDColJsName, TDbColObj[TColPropName]>;
    for (let col of cols) {
        resObj[col] = tblInfo[col][colPropName];
    }
    return resObj;
}

Version 2:

function getColKeyToPropMap<
    TDbTblJsName extends keyof DbConfig,
    TDbTblColObjRecord extends DbConfig[TDbTblJsName]["columns"],
    TDbColObj extends TDbTblColObjRecord[keyof TDbTblColObjRecord]
>(tbl: TDbTblJsName, cols: Array<keyof TDbTblColObjRecord>, colPropName: keyof TDbColObj) {
    const tblInfo: TDbTblColObjRecord = simpleDbConfig[tbl].columns as TDbTblColObjRecord;
    const resObj: Record<keyof TDbTblColObjRecord, TDbColObj[keyof TDbColObj]> = {} as Record<keyof TDbTblColObjRecord, TDbColObj[keyof TDbColObj]>;
    for (let col of cols) {
        resObj[col] = tblInfo[col][colPropName];
    }
    return resObj;
}

下面是一个示例性函数调用:

const test = getColKeyToPropMap("firstTable", ["foo"], "nameDb");

// test should be
// {foo : Foo}

在for循环中,我得到了关于这一行的以下打字错误:"resObj [列]=表信息[列][列属性名称];":
"类型" TColPropName "不能用于索引类型" TDbTblColObjRecord [TDbColJsName]"。"
我错在哪里?
感谢您的时间和帮助!
我试着用索引访问来访问嵌套类型,尽管这种访问在javascript中是有效的,但不知何故它在typescript中是无效的。

6g8kf2rb

6g8kf2rb1#

您的代码格式如下:

function getColKeyToPropMap<
    K extends keyof DbConfig,
    C extends DbConfig[K]["columns"],
    P extends keyof C,
    V extends C[P],
    Q extends keyof V
>(tbl: K, cols: P[], colPropName: Q) {
    const tblInfo: C = simpleDbConfig[tbl].columns as C;
    const resObj: Record<P, V[Q]> = {} as Record<P, V[Q]>;
    for (let col of cols) {
        resObj[col] = tblInfo[col][colPropName];  // error!
    }
    return resObj;
}

不起作用是因为tblInfo[col]的类型是C[P],而colPropname的类型是Q extends keyof V,虽然V被限制为C[P],但这并不等于说它是C[P]V extends C[P]意味着V可以拥有比C[P]更多的属性因此密钥V可能不是密钥C[P]
此代码具有的generic类型参数比函数中所需的多。通常,每个函数参数只需要一个类型参数(也有例外,但关键是你希望类型参数直接从函数参数中 * 可推断 *)如果你发现自己想添加一个类型参数只是为了成为一个讨厌的写出类型的别名,你可以通过创建一个适当的泛型类型别名来减少一些(但可能不是全部)冗余来减轻这种情况。2例如:

type ColsFor<K extends keyof DbConfig> =
    DbConfig[K]["columns"]

function getColKeyToPropMap<
    K extends keyof DbConfig,
    P extends keyof ColsFor<K>,
    Q extends keyof ColsFor<K>[P]
>(tbl: K, cols: P[], colPropName: Q) {
    const tblInfo: DbConfig[K]["columns"] = simpleDbConfig[tbl].columns;
    const resObj: Record<P, ColsFor<K>[P][Q]> = {} as Record<P, ColsFor<K>[P][Q]>;
    for (let col of cols) {
        resObj[col] = tblInfo[col][colPropName]; // okay
    }
    return resObj;
}

我使用ColsFor<K>作为DbConfig[K]["columns"]的别名,这样就不必出现很多次,这使得放弃额外的类型参数变得不那么痛苦。
现在代码起作用了;colPropName的类型是Q extends keyof ColsFor<K>[P]tblInfo[col]的类型是ColsFor<K>[P],这意味着colPropNametblInfo[col]的键,所以赋值的两边都是ColsFor<K>[P][Q],所有的编译都是按要求进行的。
Playground代码链接

相关问题