首页 > 解决方案 > 如何使用打字稿中的查找来推断类型化的 mapValues?

问题描述

如同:

如何从打字稿中的动态键数组推断类型化数组?

我正在寻找一个通用对象,它接收任意键的映射以查找值,并返回具有类型值的相同键(如类型化 _.mapValues)。

从对象获取单一类型属性的能力已被记录并有效。使用数组,您需要将重载硬编码为类型化元组,但对于对象,我会收到“重复字符串索引签名”错误。

export interface IPerson {
    age: number;
    name: string;
}

const person: IPerson = {
    age: 1,
    name: ""
}

function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
    return o[name];
}

const a = getProperty(person, 'age');
// a: number

const n = getProperty(person, 'name');
// n: string

function getProperties<T, K extends keyof T>(obj: T, keys: { [key: string]: K }) {
    const def: { [key: string]: T[K] } = {};
    return Object.entries(keys).reduce((result, [key, value]: [string, K]) => {
        result[key] = getProperty(obj, value);
        return result;
    }, def);
}

const { a2, n2 } = getProperties(person, {
    a2: 'name',
    n2: 'age'
});

// Result:
// {
//     a2: string | number, 
//     n2: string | number
// }

// What I'm looking for:
// {
//     a2: string, 
//     n2: number' 
// }

这如何用打字稿实现?

标签: typescripttypescript-typings

解决方案


只要它在运行时工作,你就可以告诉 TypeScript 如何使用映射类型重命名键:

type RenameKeys<T, KS extends Record<keyof KS, keyof T>> = {[K in keyof KS]: T[KS[K]]};

function getProperties<T, KS extends Record<keyof KS, keyof T>>(
    obj: T,
    keys: KS
): RenameKeys<T, KS> {
    const def = {} as RenameKeys<T, KS>;
    return (Object.entries(keys) as Array<[keyof KS, any]>)
      .reduce((result, [key, value]) => {
        result[key] = getProperty(obj, value);
        return result;
    }, def);
}

这应该像您在类型系统中所期望的那样运行。亮点: 的类型keys被赋予了一个名为 的类型参数KS,它被约束为Record<keyof KS, keyof T>,这或多或少的意思是“我不在乎键是什么,但属性类型需要是来自的键T”。然后,RenameKeys<T, KS>遍历它们的键KS并从中提取与它们相关的属性类型T

最后,我需要做一些类型断言... defis RenameKeys<T, KS>. 我刚刚创建的valuein的类型,因为类型系统很难验证它是否是正确的类型。所以这是对实现类型安全性的一种捏造......但调用者应该很高兴:[key, value]anyresult[key]getProperties()

const {a2, n2} = getProperties(person, {
  a2: 'name',
  n2: 'age'
});
// a2 is string, n2 is number.

Playground 代码链接


推荐阅读