typescript 两种类型的函数具有相同的参数但返回类型不同

hjzp0vay  于 2023-01-03  发布在  TypeScript
关注(0)|答案(2)|浏览(552)

我想检查传递的类型是否正确。请参见下面的代码示例。但我不知道如何根据返回类型进行检查,因为FunctionAFunctionB的参数相同。

type TypeA = { a: string };
type TypeB = { a?: string, b: Record<string, any> };

type FunctionA = ( argA: string ) => TypeA | Promise<TypeA>;

type FunctionB = ( argA: string ) => TypeB | Promise<TypeB>;

function isFunctionB(target: FunctionA | FunctionB): target is FunctionB {
  return true //How do I check the type without running the function
}
 
class Test {

    myFunction?: FunctionA | FunctionB;
    mustBeFunctionB: boolean;

    constructor({
        myFunction,
        mustBeFunctionB
    }: {
        myFunction?:  FunctionA | FunctionB;
        mustBeFunctionB: boolean;
    }) {
        this.myFunction = myFunction;
        this.mustBeFunctionB = mustBeFunctionB

        if (mustBeFunctionB && myFunction && !isFunctionB(myFunction)) {
            throw new Error(
                "Invalid constructor arguments! myFunction must be a type of FunctionB"
            );
        } else {
            console.log("Passed")
        }
    }
}

async function myFunctionA(argA: string): Promise<{ a: string }> {
  return { a: "test" };
}

const test = new Test({
  myFunction: myFunctionA,
  mustBeFunctionB: true
}); // This should fail but it still passed
kxkpmulp

kxkpmulp1#

如果不执行函数,就无法在运行时检查函数是否符合给定的签名(包括返回类型),因为那时类型信息不存在。
我能想到的解决这个问题的唯一方法是使用品牌类型,如下所示:

type TypeA = { a: string };
type TypeB = { a?: string, b: Record<string, any> };

type FunctionA = (( argA: string ) => TypeA | Promise<TypeA>) & { __brand: 'A' };

type FunctionB = (( argA: string ) => TypeB | Promise<TypeB>) & { __brand: 'B' };

// ...

const myFunctionA: FunctionA = async (argA: string): Promise<{ a: string }> => {
  return { a: "test" };
}

myFunctionA.__brand = 'A'

// ...

function isFunctionB(target: FunctionA | FunctionB): target is FunctionB {
  return target.__brand === 'B';
}

这将允许您在运行时区分这两种类型,前提是它们定义正确。
如果您打算更频繁地重用这种模式,您可能需要查看ts-brand

hgncfbus

hgncfbus2#

解决方案1:方法重载
你可以利用方法重载来实现它。下面是一个简化的代码示例来展示这个概念。

// if mustBeFunctionB is true, myFunction should be FunctionB
function fun(myFunction: FunctionB, mustBeFunctionB: true): any
// if mustBeFunctionB is false, myFunction could be FunctionA or FunctionB
function fun(myFunction: FunctionA | FunctionB, mustBeFunctionB: false): any
// the base type defination, should be compatible with all types above
function fun(myFunction: FunctionA | FunctionB, mustBeFunctionB: boolean): any {

};

它也可以用于类构造函数。请参见playground中对代码的修改
解决方案2:并集类型参数
如果您可以像代码中那样使用单个对象配置,也可以使用union声明输入,以指定mustBeFunctionB标志的对等项。

constructor({
    myFunction,
    mustBeFunctionB
}: {
    myFunction: FunctionB;
    mustBeFunctionB: true;
} | {
    myFunction?:  FunctionA | FunctionB;
    mustBeFunctionB: false;
})

运动场
要注意,这些解决方案并不检查运行时的确切上下文,而只是像你在标题中所说的“类型守卫”,然而,typescript会防止你做出错误的输入,这就是typescript的概念。

相关问题