重命名对象/接口类型安全

时间:2018-06-15 08:43:49

标签: javascript typescript typescript2.0

我想用映射函数动态重命名键。为此,我做了这个:

interface DateRange {
    startDate: string;
    endDate: string;
}

function format<K extends string>(range: DateRange, mapping: {[X in keyof DateRange]: K}): {[P in K]: string} {
    return {
        [mapping.startDate]: range.startDate,
        [mapping.endDate]: range.endDate,
    };
}

当我使用as {[P in K]: string}转换此函数的返回值时,一切正常,但没有强制转换,它不会编译。

错误讯息:

TS2322: Type '{ [x: string]: string; }' is not assignable to type '{ [P in K]: string; }'.

从我看到的DateRange接口的两个键都被使用,因此返回值的键应该都是K类型。

该函数必须如何,不需要强制转换?

示例电话:

const formatted = format(
    {startDate: 'startDateValue', endDate: 'endDateValue'}, 
    {startDate: 'start', endDate: 'end'}
);
// formatted = {end: 'endDateValue', start: 'startDateValue'}

1 个答案:

答案 0 :(得分:1)

我可以重现你的问题,而且在GitHub问题之前似乎已经noted了。这个问题已被标记为“已修复”,但这一部分显然没有。无论如何,这可能只是一个设计限制... TypeScript可以用一个通用类型参数表示一个对象的唯一方法就是使用mapped type,我怀疑TypeScript中的任何内容都会将对象文字解释为自动映射的类型。我不知道GitHub中是否存在更合适的现有问题,或者有人应该打开一个问题。

目前,您必须处理变通方法。你使用的那个很好(断言返回值的类型),如果你小心的话。 稍微更类型安全的是逐个添加属性,如:

function format<K extends string>(
  range: DateRange, 
  mapping: { [X in keyof DateRange]: K }
): { [P in K]: string } {
  const ret = {} as { [P in K]: string }; // assertion
  ret[mapping.startDate] = range.startDate; // safer
  ret[mapping.endDate] = range.endDate; // safer
  return ret;
}

如果遗漏一个属性,它仍然不会抱怨,但它会阻止你添加随机的其他字符串值计算属性:

  const badProp: string = "uh oh"
  return {
    [mapping.startDate]: range.startDate,
    [mapping.endDate]: range.endDate,
    [badProp]: "whoops" // no error
  } as { [P in K]: string };

VS

  const badProp: string = "uh oh"
  const ret = {} as { [P in K]: string };
  ret[mapping.startDate] = range.startDate;
  ret[mapping.endDate] = range.endDate;
  ret[badProp] = "whoops"; // error (implicit any)

但是,对你来说这可能不值得,你的类型断言是合理的。希望这很有帮助。祝你好运!