TypeScript函数签名与函数签名中传递的相同

时间:2017-03-17 13:23:27

标签: typescript

我正在处理一个类,用户在一个采用用户定义的参数或没有参数的方法中传递给它。该类公开了一个“调用”方法,该方法必须使用用户方法中请求的参数调用,执行各种操作,然后调用用户的方法并返回结果。

如果用户的函数有参数,则可以正常工作,但如果用户的函数不使用参数,则会中断。

这是我当前的代码(我一直尝试不同的变体,但尚未找到正确的代码):

call()
call(data: T) 
call(data?: T) {

我尝试的其中一个变体是调整呼叫的签名:

struct B1 {
    B1(int);
};
struct D1 : B1 {
    using B1::B1;
// The set of candidate inherited constructors is 
// 1. B1(const B1&)
// 2. B1(B1&&)
// 3. B1(int)

// D1 has the following constructors:
// 1. D1()
// 2. D1(const D1&) 
// 3. D1(D1&&)
// 4. D1(int) <- inherited
};

struct B2 {
    B2(int = 13, int = 42);
};
struct D2 : B2 {
    using B2::B2;
// The set of candidate inherited constructors is
// 1. B2(const B2&)
// 2. B2(B2&&)
// 3. B2(int = 13, int = 42)
// 4. B2(int = 13)
// 5. B2()

// D2 has the following constructors:
// 1. D2()
// 2. D2(const D2&)
// 3. D2(D2&&)
// 4. D2(int, int) <- inherited
// 5. D2(int) <- inherited
};

然而,这导致了test.call()和test4.call()都被允许。 如何获取打字稿以确保调用签名始终与用户函数的签名匹配?

2 个答案:

答案 0 :(得分:1)

以下是new ()

的方法
interface OneOrNoArgFunc<T, TResult> {
    (arg?: T): TResult;
}

class Test<T, TResult, TF extends OneOrNoArgFunc<T, TResult>> {
    methodToRun: TF;

    constructor(methodToRun: TF) {
        this.methodToRun = methodToRun;
    }

    call = ((arg?: T): TResult => {
        return this.methodToRun(arg);
    }) as TF;
}

let a = new Test((foo: string) => foo + "hello");
let b = new Test(() => 33);
let c = new Test((foo: string, bar: number) => foo + bar); // ERR, cannot have more than one argument

a.call("argument"); // OK
a.call(33); // ERR, wrong type
a.call(); // ERR, need argument

b.call();
b.call("argument"); // ERR, no arguments

这确实需要明确as TF强制转换,而且泛型不是非常有用的:打字稿标记aa: Test<{}, {}, (foo: string) => string>,似乎推断的泛型不是&#39; t通过了这个函数。

修改

您可以使用交集类型保留参数和返回类型通用参数:

constructor(methodToRun: ((arg?: T) => TResult) & TF) {
    this.methodToRun = methodToRun;
}

/* ... */

// a: Test<string, string, (foo: string) => string>
let a = new Test((foo: string) => foo + "hello");

// b: Test<{}, number, () => number>
let b = new Test(() => 33);

答案 1 :(得分:0)

这有点冗长,但它适用于您的要求:

interface TestWithParams<T, TResult> {
    call(data: T): TResult;
}

interface TestWithoutParams<T, TResult> {
    call(): TResult;
}

interface TestConstructor {
    new<T, TResult>(methodToRun: () => TResult): TestWithoutParams<T, TResult>;
    new<T, TResult>(methodToRun: (data: T) => TResult): TestWithParams<T, TResult>;
}

class Test<T, TResult> {
    methodToRun: (data?: T) => TResult;

    constructor(methodToRun: (data?: T) => TResult) {
        this.methodToRun = methodToRun;
    }

    call(data?: T) {
        // do some stuff, then...
        return data === undefined ? this.methodToRun(data) : this.methodToRun();
    }
}

const test = new (Test as TestConstructor)(function (data: string) {
    return 1;
});

test.call("");
test.call(); // error

const test2 = new (Test as TestConstructor)(function (data: number) {
    return 1;
});

test2.call(1);

const test3 = new (Test as TestConstructor)(function (data: { foo: string, bar: number }) {
    return 1;
});

test3.call({foo: "", bar: 1});

const test4 = new (Test as TestConstructor)(function () {
    return 1;
});

test4.call(); // ok
test4.call({}); // error

code in playground

修改

在没有(Test as TestConstructor)的情况下,我能想到的唯一方法是使用&#34;工厂方法&#34;:

class Test<T, TResult> {
    methodToRun: (data?: T) => TResult;

    static create<T, TResult>(methodToRun: () => TResult): TestWithoutParams<T, TResult>;
    static create<T, TResult>(methodToRun: (data: T) => TResult): TestWithParams<T, TResult>;
    static create<T, TResult>(methodToRun: (data?: T) => TResult): Test<T, TResult> {
        return new Test(methodToRun);
    }

    private constructor(methodToRun: (data?: T) => TResult) {
        this.methodToRun = methodToRun;
    }

    call(data?: T) {
        // do some stuff, then...
        return data === undefined ? this.methodToRun(data) : this.methodToRun();
    }
}

然后:

const test = Test.create(function (data: string) {
    return 1;
});

...

code in playground

这似乎非常优雅,这为Test引入了一个额外的功能,它也将存在于已编译的js中。
以前的方式是严格的打字稿,并没有导致更多&#34;膨胀&#34; JS。