如何使用typescript中的查找推断类型化的mapValues?

时间:2018-06-12 17:29:24

标签: typescript typescript-typings

类似于:

How to infer a typed array from a dynamic key array in typescript?

我正在寻找一个通用对象,它接收任意键的映射来查找值,并返回具有类型值的相同键(如类型的_.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' 
// }

如何使用打字稿来实现?

2 个答案:

答案 0 :(得分:3)

可能有一种方法可以优化/折叠这种类型,但我有getProperties的函数原型,我相信它可以实现你想要的。

定义中缺少的是一个强大的返回类型定义,它将keys对象中特定键与obj对象中特定类型之间的关联链接起来。因为缺少这一点,一切都变成了类型的联合,这就是你在上面看到的行为。

我提出的功能类型是:

function getProperties
    <T, K extends keyof T, U extends { [name: string]: K }>
        (obj: T, keys: U):
            {[V in keyof U]: T[U[V]];

这里的重要部分是返回值类型:{[V in keyof U]: T[U[V]]}

它为V对象中的每个键keys指定:

  1. V将成为输出对象中的键

  2. 值类型将是输入obj中的类型,其中类型来自与V中与键U关联的值指定的键。

答案 1 :(得分:3)

只要它在运行时工作,就可以告诉TypeScript如何使用mapped types重命名密钥:

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).reduce((result, [key, value]: [keyof KS, any]) => {        
    result[key] = getProperty(obj, value);
    return result;
  }, def);
}

这应该在类型系统中按预期运行。要点:keys的类型被赋予一个名为KS的类型参数,该参数被约束为Record<keyof KS, keyof T>,这或多或少意味着&#34;我不在乎什么密钥是,但属性类型需要是T&#34;的密钥。然后,RenameKeys<T, KS>遍历KS的密钥,并从与其相关的T中提取属性类型。

最后,我需要做一些类型的断言...... defRenameKeys<T, KS>。我刚才value [key, value]any的类型result[key],因为类型系统很难确认getProperties()是正确的类型。所以它对实现类型的安全性有点捏造......但是const {a2, n2} = getProperties(person, { a2: 'name', n2: 'age' }); // a2 is string|number, n2 is string|number 的调用者应该感到高兴:

const {a2, n2} = getProperties(person, {
  a2: 'name' as 'name',
  n2: 'age' as 'age',
});
// a2 is string, n2 is number.
等等,什么?哦,那是因为TypeScript doesn't narrow properties to string/numeric literals automatically。解决方法是明确断言缩小类型,以避免不幸的重复:

{{1}}

希望有所帮助!