我有一个服务调用,它给了我一个对象,我想在该对象中存储更多数据。我已经尽可能地抽象了它:
游乐场连接
//in the real sample, I1 and the serviceCall are generated and I do not want to change them
interface I1{ name: string; }
function serviceCall():I1 {return {name:"Hello World"};}
const o1:I1 = serviceCall();
interface I2 { age: number; }
// I want to store the data of I2 in the same object, so my target type is (I1&I2)
type target = (I1&I2);
//solution 1: this copies all of o1 into a new object. I want no extra copy!
const o2copy:target = {...o1, age: 144};
//solution 2: casting
const o2cast:target = o1 as target;
//works but does not check anything!
//age is undefined until I set it but it does not give me any error if I leave the next line!
o2cast.age = 144;
//solution 3: simply setting the property: the javascript is fine, but typescript yields errors
o1.age = 144; //ERROR: property age does not exist on I1
const o2setprop:target = o1;
解决方案3将是我最喜欢的,我如何才能编译它?
您可以使用Object.assign()
将属性从{age: 144}
复制到目标o1
对象中。此函数返回o1
,标准库的类型签名的返回类型是交集类型,如所需:
const o2: target = Object.assign(o1, { age: 144 });
console.log(o2.name.toUpperCase()); // HELLO WORLD
console.log(o2.age.toFixed(1)); // 144.0
请注意,交集只是Object.assign()
返回的实际类型的近似值。如果传递具有冲突属性的参数,则交叉点可能不准确。然而,在您的示例代码中,这不是一个问题。
不过,编译器仍然会将o1
视为类型I1
,因此您将不得不开始仅使用o2
,而忘记o1
:
o1.age; // compiler error!
// ~~~ <-- I1 has no property named "age"
如果您真的想保留o1
而不必使用新的变量名,您可以考虑使用TypeScript 3.7中引入的断言函数。Object.assign()
没有内置版本可以充当断言函数,但您可以编写一个:
function assign<T, U>(target: T, source: U): asserts target is T & U {
Object.assign(target, source);
}
assign
的返回类型为asserts target is T & U
。因此,在调用assign(o1, {age: 144}
之后,编译器将把o1
视为I1 & {age: number}
:
assign(o1, { age: 144 });
console.log(o1.name.toUpperCase()); // HELLO WORLD
console.log(o1.age.toFixed(1)); // 144.0
这可能是您想要的方式,但请记住,断言函数实现不会被编译器验证为类型安全。这样一个函数的作者有责任确保在调用该函数后,在其返回类型中断言的类型缩小实际发生。例如,如果你没有在函数内部做任何事情,没有什么会警告你:
function badAssign<T, U>(target: T, source: U): asserts target is T & U { }
在这种情况下,你在badAssign()
中对编译器撒谎,任何使用它的人都会遇到麻烦:
badAssign(o1, { oops: "uh oh" });
o1.oops.toUpperCase(); // no compiler error, but runtime error!
所以要注意这种方法。
到代码的游乐场链接
我想你可以这样做:
const o2setprop:target = setProp(o1, "age", 144);
function setProp<T, P, K extends keyof any>(obj: T, k: K, val: P): T & { [_ in K]: P } {
const o = obj as any
o[k] = val
return o
}