在类型之间映射键

时间:2018-05-04 02:01:22

标签: typescript

考虑下面的Typescript。我们有两个接口,两个接口都有我们想要映射的相同字段。即我想采用SerialisedModel的实例并将其转换为Model。在下面的示例中,如果我重命名SerialisedModelModel上的字段,编译器将不会抱怨并且我们会遇到运行时问题。

我真正想要做的是声明Model具有与SerialisedModel相同的键,但允许不同的值。例如。 agestring中的SerialisedModel,但number中的Model

我似乎无法弄清楚如何使用任何Typescript语法执行此操作。我们设法让它在某种程度上与Mapped类型一起使用,但它总是以值any结束。有什么想法吗?



interface SerialisedModel {
  name: string;
  age: string;
  dateOfBirth: string;
}

interface Model {
  name: string;
  age: number;
  dateOfBirth: Date
}

const f: SerialisedModel = // instanciate SerialisedModel;
let b = {} as Model;
Object.keys(f).forEach(key => {
  const mapped = f[key] // then do some mapping to convert values
  b[key] = mapped;
})




1 个答案:

答案 0 :(得分:1)

我对你在这里真正想要达到的目标感到困惑。你在哪里看到"它总是以值any"?我在我或您的代码中看不到这一点。

如果你想要做的就是让编译器对你大喊大叫如果两个类型有不同的键,你可以这样做:

type RequireSameKeys<T extends Record<keyof U, any>, U extends Record<keyof T, any>> = true

type VerifyModelTypes = RequireSameKeys<SerialisedModel, Model>; // okay?

如果VerifyModelTypes没有错误,则SerialisedModelModel具有完全相同的密钥。我们来看看:

interface Model {
  name: string;
  age: number;
  dateOfBirth: Date
}

interface SerialisedModel {
  name: string;
  age: string;
  dateOfBrith: string; // Hppay Brithday To Yuo
}

type VerifyModelTypes = RequireSameKeys<SerialisedModel, Model>; // error!
//  Property 'dateOfBirth' is missing in type 'SerialisedModel'.

哎呀,让我们解决一下:

interface SerialisedModel {
  name: string;
  age: string;
  dateOfBirth: string; // fixed
}

type VerifyModelTypes = RequireSameKeys<SerialisedModel, Model>; // okay

现在开心了。

但由于SerialisedModel似乎是Model的简单映射,您确实应该能够使用映射类型来定义它:

type _SM = { [K in keyof Model]: string };
interface SerialisedModel extends _SM {};  // same as before

然后创建一个通用的处理器函数来在它们之间进行转换:

function makeProcessor<T extends Record<keyof T & keyof U, any>, 
  U extends Record<keyof T & keyof U, any>>(
  processors: { [K in keyof T & keyof U]: (x: T[K]) => U[K] }
): (t: T) => U {
  return (t: T) => {
    const ret = {} as U;
    // note Object.keys() returns string[], not (keyof T)[], so we assert
    Object.keys(processors).forEach((k: keyof T) => {
      ret[k] = processors[k](t[k]);
    });
    return ret;
  }
}

以下是您如何使用它:

const deserialise = makeProcessor<SerialisedModel, Model>({
  age: a => +a,
  name: n => n,
  dateOfBirth: d => new Date(+d)
});

const serialise = makeProcessor<Model, SerialisedModel>({
  age: a => "" + a,
  name: n => n,
  dateOfBirth: d => "" + d.getTime()
})

const model: Model = {
  name: "Gabriel Jarret",
  age: 48,
  dateOfBirth: new Date(0)
}

console.log(model.name); // "Gabriel Jarret"
console.log(model.age); // 48 
console.log(model.dateOfBirth); // Date 1970-01-01T00:00:00.000Z

const serialisedModel = serialise(model);

console.log(serialisedModel.name); // "Gabriel Jarret"
console.log(serialisedModel.age); // "48"
console.log(serialisedModel.dateOfBirth); // "0"

const deserialisedModel = deserialise(serialisedModel);

console.log(deserialisedModel.name === model.name); // true
console.log(deserialisedModel.age === model.age); // true
console.log(deserialisedModel.dateOfBirth.getTime() === 
  model.dateOfBirth.getTime()); // true

这对我来说都很好看。这有帮助吗?如果您继续发现问题,请包含发生错误的特定代码。否则,我只是猜测你需要什么。祝你好运!