挑战

实现一个向接口添加新字段的类型。该类型接受三个参数。输出应该是一个带有新字段的对 象。

例如:

type Test = { id: "1" };
type Result = AppendToObject<Test, "value", 4>; // expected to be { id: '1', value: 4 }

解答

当我们试图改变 TypeScript 中的对象接口时,通常交叉(intersection)类型会很有帮 助。这一挑战也不例外。我试着写了一个类型,它接受整个T和一个带有新属性的对象:

type AppendToObject<T, U, V> = T & { [P in U]: V };

不幸的是,这个方案不能满足测试。它们期望的是一个普通(flat)类型而不是交叉 (intersection)类型。因此我们需要返回一个对象类型,其中包含所有属性和我们的新属 性。我将从映射T的属性开始:

type AppendToObject<T, U, V> = { [P in keyof T]: T[P] };

现在,我们需要在T的属性中添加新属性U。这里有一个诀窍。没有什么可以阻止你将联 合传递给in操作符:

type AppendToObject<T, U, V> = { [P in keyof T | U]: T[P] };

这样,我们就能得到T的所有属性加上属性U,这正是我们需要的。现在让我们通过 在U上添加一个约束来修复小错误:

type AppendToObject<T, U extends string, V> = { [P in keyof T | U]: T[P] };

现在 TypeScript 唯一不能处理的是P可能不在T中,因为PTU的并集。我们 需要处理下这种情况,如果P来自于T,我们取T[P],否则取V

type AppendToObject<T, U extends string, V> = {
  [P in keyof T | U]: P extends keyof T ? T[P] : V;
};

参考