从一种类型转换为另一种类型

时间:2020-07-11 06:21:22

标签: typescript types

我有一个类型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之前,我只能在其中添加第一个属性。

1 个答案:

答案 0 :(得分:1)

这取决于您的用例:例如calc函数仅需要UncalculatedType还是calcB还需要calcA的结果?

如果calcB不需要calcA的结果,则可以从各个函数返回intersection types

请参阅TypeScript Playground

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
}

我个人更喜欢最后一种解决方案,因为每个函数都简洁明了,只获取所需的输入参数,并输出单个计算值。这样,这些函数易于测试且易于重用(它们与您的接口定义无关)。
但是如上所述,这取决于用例。