【问题标题】:Create TypeSafe Object literal based upon existing object in TypeScript基于 TypeScript 中的现有对象创建 TypeSafe 对象字面量
【发布时间】:2018-12-13 01:04:18
【问题描述】:

考虑以下函数:

function keysToValuesClone<TObject extends {}>(keysAndValuesObj: TObject) {
    const clone = Object.assign({}, keysAndValuesObj);

    Object.keys(clone).forEach(key => clone[key] = key);

    const frozen = Object.freeze(clone);

    return (<any>frozen) as Readonly<{ // The `any` is regrettable... see below
        [K in keyof typeof clone]: K
    }>;
}

我可以为这个函数提供一个像这样的对象字面量:

const myHeaders = keysToValuesClone({
    'a property with spaces': '',
    'and another': '',
});

而产生的myHeaders将有这种类型:

Readonly<{
    'a property with spaces': "a property with spaces";
    'and another': "and another";
}>

我喜欢这个函数的实用性,但写它在我脑海中提出了两个问题:

是否有可能丢失any

如果我将keysToValuesClone 的最后一行更改为:

    return frozen as Readonly<{
        [K in keyof typeof clone]: K
    }>;

然后我遇到以下错误:

Type 'Readonly<TObject & {}>' cannot be converted to type 'Readonly<{ [K in keyof (TObject & {})]: K; }>'.
  Type '(TObject & {})[P]' is not comparable to type 'P'.

我已经尝试以多种不同的方式说服编译器在没有any 类型断言的情况下允许此函数,但所有尝试都失败了。这可以改进吗?你能说服编译器在不求助于any 的情况下顺其自然吗?

是否可以创建一个类似的函数来从 string[] 构造此类型?

我很想写这段代码:

const myHeaders = keysToValuesUsingArray([
    'a property with spaces',
    'and another'
]);

即提供一个字符串数组并使其输出与上述相同。 Codewise 我可以这样实现:

function keysToValuesArray(keys: string[]) {
    const obj = keys.reduce((objInTheMaking, key) => ({ [key]: key, ...objInTheMaking  }), {});

    return obj;
}

但这会产生{} 类型。有没有办法在类型系统中提示这里发生了什么?

我玩过keyof / typeof / Tuple 等都无济于事。所以我的朋友们,我求助于你。

教育我。


感谢提香!

太棒了提香;非常感谢!我最终通过 Readonly 调整推出了您的阵列解决方案;见下文:(我现在莫名其妙地爱上了Object.freeze;这是我的果酱)

function keysToValuesArray<T extends string>(keys: T[]): Readonly<{ [P in T] : P}> {
    const obj = keys.reduce((objInTheMaking, key) => ({ [key]: key, ...objInTheMaking  }), {} );

    const frozen = Object.freeze(obj);

    return frozen as Readonly<{ [P in T] : P}>;
}

再次感谢!

【问题讨论】:

    标签: typescript types


    【解决方案1】:

    如果我们使用泛型类型参数来表示字段名称,您当然可以从string 数组创建对象:

    function keysToValuesArray<T extends string>(keys: T[]) : { [P in T] : P}{
        const obj = keys.reduce((objInTheMaking, key) => ({ [key]: key, ...objInTheMaking  }), {} );
        return obj as { [P in T] : P};
    }
    const myHeaders2 = keysToValuesArray([
        'a property with spaces',
        'and another'
    ]);
    

    为了避免强制转换为any,您可以直接将clone 输入为any{ [n: string]: any },但这不会更好。在构建这些对象时,它们无论如何都不符合类型,因此没有太大区别。我通常认为调用站点是类型安全的,这一点更重要。

    function keysToValuesClone<TObject extends {}>(keysAndValuesObj: TObject) {
        const clone : { [n: string]: any } = Object.assign({}, keysAndValuesObj);
    
        Object.keys(clone).forEach(key => clone[key] = key);
    
        const frozen = Object.freeze(clone);
    
        return frozen as Readonly<{ 
            [K in keyof typeof clone]: K
        }>;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-14
      • 2012-03-01
      • 1970-01-01
      • 2020-05-02
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      • 1970-01-01
      相关资源
      最近更新 更多