用匹配动态子项的键扩展返回对象

时间:2018-10-31 20:51:10

标签: typescript types

我想拥有一个函数,该函数带有一个带有可选键(inst)的对象,然后该对象将使用inst对象的键来创建返回对象-例如:< / p>

function init( db, config ) {
    let ret = {};

    if ( config.inst ) {
        for (let [key, value] of Object.entries(config.inst)) {
            ret[ key ] = value+1; // would normally do some kind of processing here
        }
    }

    return ret;
}

如果使用init( db, { inst: { a: 1, b: 2 } } );进行了调用,它将返回{ a: 2, b: 3 }。是的,它有点多余,但是它可以简化我的想法!

虽然很容易用Java编写代码,但我终生无法找到我应该为此函数定义的接口,以让TypeScript处理返回的数据和类型。

让我烦恼的关键是如何获取inst嵌套对象的密钥。我知道我可以使用keyofin来获取对象的键,但是在这里看不到如何应用它。

确实非常欢迎任何帮助!

跟进-更改返回属性的类型

作为后续操作,此答案假定返回的参数类型将与为inst传递的参数匹配。如果更改类型怎么办?例如将数字更改为字符串,反之亦然?

type Config<T extends {}> = {
    inst?: T
}

function init<T extends {}>(db, config: Config<T>): T & {} {
    let ret: any = {};

    if ( config.inst ) {
        for (let [key, value] of Object.entries(config.inst)) {
            if ( typeof value === 'number' ) {
                ret[ key ] = value.toString();
            }
            else {
                ret[ key ] = parseInt( value, 10 );
            }
        }
    }

    return ret;
}


let { num, str } = init( null, { inst: { num: '1', str: 2 } } );

这样,被破坏的num将是一个字符串,而str将是一个数字,对于该函数执行的“处理”而言,这是不正确的。有没有办法让TypeScript推断正确的类型?

2 个答案:

答案 0 :(得分:1)

如果我对您的理解正确,则类似的内容应该可以代表您的工作,因此您实际上不需要执行任何操作即可正确传播类型信息:

type Config<T extends {}> = {
    inst?: T
}

function init<T extends {}>(db, config: Config<T>): T | {} {
    let ret = {};

    if ( config.inst ) {
        for (let [key, value] of Object.entries(config.inst)) {
            ret[ key ] = value+1; // would normally do some kind of processing here
        }
    }

    return ret;
}

对于错误的情况,您可能应该返回空对象(例如,未定义)以外的其他内容,以便以后以TypeScript理解的方式更容易地检查发​​生了哪种情况。

答案 1 :(得分:1)

如果您需要更改成员的类型,则需要在返回类型中添加一些映射类型和条件类型(有关它们的文档,请参见here)。您将需要根据条件类型来表达您执行的处理(实际上是根据类型来重复处理算法),但至少调用者会获得适当的类型:

type Config<T extends {}> = {
    inst?: T
}

function init<T extends {}>(db, config: Config<T>): {
  [P in keyof T]: // take each property of T
    T[P] extends number ? string : number // If the property T[P] is a number the new property type will be string otherwise it will be number
} {
    let ret: any = {};

    if ( config.inst ) {
        for (let [key, value] of Object.entries(config.inst)) {
            if ( typeof value === 'number' ) {
                ret[ key ] = value.toString();
            }
            else {
                ret[ key ] = parseInt( value, 10 );
            }
        }
    }

    return ret;
}


let { num, str } = init( null, { inst: { num: '1', str: 2 } } );