Typescript 使用工厂方法和已知输入参数进行扩展类型检查

Typescript 使用工厂方法和已知输入参数进行扩展类型检查,typescript,Typescript,请原谅我的标题可能不好,不知道如何描述: 我有接口A和B: interface A { prop1: string prop2: object } interface B { prop3: number } interface C extends A { prop2: { objectProperty: string } } function func1(arg1: string, arg2: object): A { ret

请原谅我的标题可能不好,不知道如何描述:

我有接口AB

interface A {
    prop1: string
    prop2: object
}

interface B {
    prop3: number
}
interface C extends A {
    prop2: {
        objectProperty: string
    }
}
function func1(arg1: string, arg2: object): A {
    return {
        prop1: arg1,
        prop2: arg2
    }
}
function func2(arg1: number): B {
    return {
        prop3: arg1
    }
}
界面C,进一步细化了A

interface A {
    prop1: string
    prop2: object
}

interface B {
    prop3: number
}
interface C extends A {
    prop2: {
        objectProperty: string
    }
}
function func1(arg1: string, arg2: object): A {
    return {
        prop1: arg1,
        prop2: arg2
    }
}
function func2(arg1: number): B {
    return {
        prop3: arg1
    }
}
然后我有一个函数,返回类型为A的对象:

interface A {
    prop1: string
    prop2: object
}

interface B {
    prop3: number
}
interface C extends A {
    prop2: {
        objectProperty: string
    }
}
function func1(arg1: string, arg2: object): A {
    return {
        prop1: arg1,
        prop2: arg2
    }
}
function func2(arg1: number): B {
    return {
        prop3: arg1
    }
}
另一个返回类型为B的对象:

interface A {
    prop1: string
    prop2: object
}

interface B {
    prop3: number
}
interface C extends A {
    prop2: {
        objectProperty: string
    }
}
function func1(arg1: string, arg2: object): A {
    return {
        prop1: arg1,
        prop2: arg2
    }
}
function func2(arg1: number): B {
    return {
        prop3: arg1
    }
}
最后是一个函数,它应该返回CB类型的对象,并使用前两个函数构建它们:

function func3(): C | B {
    if(...) {
        return func1('some string', { objectProperty: 'another string' });
    } else {
        return func2(100);
    }
}
我想要实现的是,确保第一个分支的返回值与类型C进行检查,如果没有检查,则得到警告。例如:

        return func1('some string', { someOtherProperty: 'another string' });
应产生警告,因为类型C中不存在someOtherProperty


不确定这是否有意义。。。这背后的原因是,我有多个接口扩展了一个,我希望有一个工厂方法来使用给定的输入对象构建它们,并且仍然能够确保生成的对象是我想要的。可能只是因为我想把一个简单的问题变成一个复杂的问题。。。但是我想这样应该是可能的?

我将更改示例代码以突出这个问题。这里我们有一个接口
A
,以及它的两个不同扩展
B
C
,其中
A
中的一个属性被缩小:

interface A {
    prop1: string
    prop2: object
}

interface B extends A {
    prop2: {
        bProp: string;
    }
}

interface C extends A {
    prop2: {
        cProp: string
    }
}
函数
makeA
接受与
A
prop1
prop2
对应的参数,并返回
A
类型的值:

function makeA(arg1: string, arg2: object): A {
    return {
        prop1: arg1,
        prop2: arg2
    }
}
但是,如果我们试图使用它来生成强类型的
B
C
实例,我们就会遇到问题:

function makeBOrC(): B | C {
    if (Math.random() < 0.5) {
        return makeA('', { bProp: '' }); // error! A is not assignable to B | C
    } else {
        return makeA('', { cProp: '' }); // error! A is not assignable to B | C
    }
}
as B
as C
告诉编译器将它认为的仅仅是类型
A
并分别将其视为A
B
或A
C
。这会使错误消失。但是请注意,当您使用类型断言时,您正在将验证类型安全性的责任从编译器转移到您身上。如果你犯了一个错误,最后对编译器撒谎,它就抓不住了。。。因此,类型断言最好谨慎使用。上面带有
{oops:123}
的一行显示了这样一种情况,即我们对编译器撒谎,并且没有错误


修正2:泛型

但是,对于本例,修复方法是在第二个参数的类型
T
中生成
makeA()

function makeA<T extends object>(arg1: string, arg2: T) {
    return {
        prop1: arg1,
        prop2: arg2
    }
}
因此,
makeA()
的输出类型取决于
arg2
的输入类型,因此应该可以跟踪
arg2
的类型。请注意,没有明确说明
makeA()
生成
A
;但你会发现,打字脚本使得这种明确性变得不必要。编译器理解
makeA()
生成可分配给
A
的值:

function foo<T extends object>(x: T) {
    const a: A = makeA("", x); // okay
}
在这里,您可以看到编译器对带有
bProp
cProp
的行很满意,因为它知道它们生成了可分配给
B | C
的内容。更好的是,带有
oops
的行会导致一个错误:编译器通过警告您输出类型不可分配给
B | C
来维护类型安全



编译器不知道
func1()
返回类型为
C
的值。如果您希望这是真的,您需要更改它的类型签名,可能类似于。这能满足你的需要吗?如果是的话,我会写一个答案;如果没有,请详细说明,提供一个例子,说明什么应该成功,什么应该失败。祝你好运那可能行得通。问题是,
func1
将返回许多扩展类型。签名不好看。然后又一次不确定这个想法是否有什么好处:-)也许你希望
func1()
像那样是泛型的?一个好的解决方案应该有足够的用例信息,这样就可以直观地看到一个提议的解决方案是否满足您的需求。如果您有多个扩展
A
的接口,并且需要
func1()
根据其输入输出所有接口,则至少显示其中的两个接口。这正是我所需要的,您完成了示例!非常感谢,很抱歉没有提供更好的第一手例子。我仍然在学习typescript,我试图找出如何使用泛型来完成它,但我没能把它做好。你想回答这个问题,我会接受它,还是我应该自己回答(当然是你的功劳)?