根据字符串文字参数键入callback参数

时间:2017-07-06 08:33:23

标签: javascript typescript

我正在尝试键入一些函数以在使用函数时获取正确的类型,并且在此使用点上显式键入最少。本质上该函数如下,我的目标是根据作为arg参数传递的字符串键入回调函数的fntype

fn(fntype: string, callback: (arg: any) => void): void;

例如,

fn('foo', (foo) => {
    foo.somethingInTheFooInterface;
}

fn('bar', (bar) => {
    bar.somethingInTheBarInterface;
}

这些是我提出的类型:

type FooType = "FooType";
const FooType: FooType = 'FooType';

type BarType = 'BarType';
const BarType: BarType = 'BarType';

type ActionTypes = FooType | BarType;

interface Action<T> {
    type: T;
}

interface FooInterface extends Action<FooType> {
    somethingOnTheFooInterface: string;
}

interface BarInterface extends Action<BarType> {
    somethingOnTheBarInterface: string;
}

type CallbackTypes = FooInterface | BarInterface;

type Callback<T extends CallbackTypes> = (action: T) => void;

function fn<T extends CallbackTypes, U extends ActionTypes>(actionType: U, cb: Callback<T>): void;

function fn (actionType, cb) {
    cb();
}

明确使用内容时哪种方法可以正常工作:

// Works fine if we explicitly type the arg
fn(FooType, (arg: FooInterface) => {
    arg.somethingOnTheFooInterface
});

// Works fine if we define the generics when calling 
fn<FooInterface, FooType>(FooType, arg => {
    arg.somethingOnTheFooInterface;
});

但是没有根据第一个参数键入回调:

// TypeError as arg is typed as the union type CallbackTypes
fn(FooType, arg => {
    arg.somethingOnTheFooInterface
})

如果有人可以就如何实现这种打字提供任何指导,那么我将非常感激。

2 个答案:

答案 0 :(得分:1)

如果我理解正确,那么这似乎是一种重大的矫枉过正 您应该能够通过签名重载实现目标:

interface FooInterface {
    somethingOnTheFooInterface: string;
}

interface BarInterface {
    somethingOnTheBarInterface: string;
}

fn(fntype: "FooType", callback: (arg: FooInterface) => void): void;
fn(fntype: "BarType", callback: (arg: BarInterface) => void): void;
fn(type: string, callback: (arg: any) => void) { ... }

答案 1 :(得分:0)

从Typescript 2.9开始,在没有函数重载的情况下,可以使用条件类型:

type FooType = "FooType";
const FooType: FooType = "FooType";

type BarType = "BarType";
const BarType: BarType = "BarType";

type ActionTypes = FooType | BarType;

interface Action<T> {
    type: T;
}

interface FooInterface extends Action<FooType> {
    somethingOnTheFooInterface: string;
}

interface BarInterface extends Action<BarType> {
    somethingOnTheBarInterface: string;
}

type CallbackTypes<T> =
    T extends FooType ? FooInterface :
    T extends BarType ? BarInterface :
    never;

type Callback<T> = (action: T) => void;

function fn<T extends ActionTypes, U extends CallbackTypes<T>>(actionType: T, cb: Callback<U>) {
    cb({} as any);
};

fn(FooType, arg => {
    arg.somethingOnTheFooInterface
})

fn(BarType, arg => {
    arg.somethingOnTheBarInterface
})