我有一个类型UncalculatedType
,它表示带有值的对象。
我的程序将使用该值并基于该值计算其他属性,然后将其附加到对象,从而创建一个CalculatedType
。这些属性是按系列计算的,将一一附加。
interface UncalculatedType {
value: string
}
interface CalculatedType {
value: string
calculatedProperty: number
anotherCalculatedProperty: string[]
}
尝试从一种类型转换为另一种类型时出现问题:
let myThing: UncalculatedType = { value: "something" }
myThing.calculatedProperty = calc(myThing.value)
// Error: Property 'calculatedProperty' does not exist on type 'UncalculatedType'.
(myThing as CalculatedType).calculatedProperty = calc(myThing.value)
mything.anotherCalculatedProperty = calc2(myThing.calculatedProperty)
// Error: Property 'anotherCalculatedProperty' does not exist on type 'UncalculatedType'.
即使在弹出类型断言之后,myThing
仍被视为UncalculatedType
。
解决此问题的最佳方法是什么?
我可以使另一个对象保存新值:
let myNewThing: CalculatedType = {
...myThing,
calculatedProperty: calc(myThing.value)
}
...但是在Typescript抱怨CalculatedType
缺少anotherCalculatedProperty
之前,我只能在其中添加第一个属性。
答案 0 :(得分:1)
这取决于您的用例:例如calc函数仅需要UncalculatedType
还是calcB
还需要calcA
的结果?
如果calcB
不需要calcA
的结果,则可以从各个函数返回intersection types:
interface UncalculatedType {
value: string
}
function calcA<T extends UncalculatedType>(foo: T): T & {
calculatedProperty: number
} {
return {
...foo,
calculatedProperty: 12
}
}
function calcB<T extends UncalculatedType>(foo: T): T & {
anotherCalculatedProperty: string[]
} {
return {
...foo,
anotherCalculatedProperty: ['a', 'b']
}
}
const foo: UncalculatedType = {
value: 'string-value'
}
const fooA = calcA(foo);
console.log(fooA.value);
console.log(fooA.calculatedProperty);
const fooB = calcB(fooA);
console.log(fooB.value);
console.log(fooB.calculatedProperty);
console.log(fooB.anotherCalculatedProperty);
当calcB
需要calcA
的结果时,您可以使用Pick<>:请参见TypeScript Playground
interface CalculatedType {
value: string
calculatedProperty: number
anotherCalculatedProperty: string[]
}
function calcA(foo: Pick<CalculatedType, 'value'>): Pick<CalculatedType, 'value' | 'calculatedProperty'> {
return {
...foo,
calculatedProperty: 12
}
}
function calcB(foo: Pick<CalculatedType, 'value' | 'calculatedProperty'>): CalculatedType {
return {
...foo,
anotherCalculatedProperty: ['a', 'b']
}
}
const fooA = calcA({
value: 'string-value'
});
console.log(fooA.value);
console.log(fooA.calculatedProperty);
const fooB = calcB(fooA);
console.log(fooB.value);
console.log(fooB.calculatedProperty);
console.log(fooB.anotherCalculatedProperty);
最后是一个简单的解决方案(仅出于完整性考虑)-TypeScript Playground:
interface CalculatedType {
value: string
calculatedProperty: number
anotherCalculatedProperty: string[]
}
function calcA(value: string) {
return 12;
}
function calcB(value: string, calculatedProperty: number) {
return ['a', 'b'];
}
const value = 'string-value';
const calculatedProperty = calcA(value);
const anotherCalculatedProperty = calcB(value, calculatedProperty);
const calculatedType: CalculatedType = {
value,
calculatedProperty,
anotherCalculatedProperty
}
我个人更喜欢最后一种解决方案,因为每个函数都简洁明了,只获取所需的输入参数,并输出单个计算值。这样,这些函数易于测试且易于重用(它们与您的接口定义无关)。
但是如上所述,这取决于用例。