在通用函数中将å‚数回调签å推断为元组?

时间:2019-02-27 03:47:52

标签: typescript generics typescript-generics

从a previous question of mine开始,我希望将作为å‚数的通用å‚数类型的映射转å‘为作为å‚数传递的回调。我的愿望是传递任æ„æ•°é‡çš„构造函数,使用这些构造函数构造实例,然åŽå°†å®žä¾‹è½¬å‘给回调。

我目å‰æ­£åœ¨æ‰§è¡Œæ­¤æ“作,其中å‚æ•°åªæ˜¯å›žè°ƒçš„数组,但是在语法上ä¸æ–¹ä¾¿ï¼Œå¹¶ä¸”TypeScript在验è¯æ供的回调具有正确签åæ–¹é¢ä¹Ÿæ²¡æœ‰æ供任何工具帮助。如果您看下é¢çš„代ç ï¼Œæ‚¨åº”该会明白我的æ„图。我希望能够为回调(而ä¸æ˜¯æ•°ç»„)指定常规å‚数,并且如果回调具有ä¸å…¼å®¹çš„ç­¾å,则会出现TypeScript错误。

TypeScriptå¯ä»¥è¿™æ ·åšå—?如果å¯ä»¥ï¼Œæ€Žä¹ˆåŠžï¼Ÿ

class Component {}
class One extends Component { public a = 1; }
class Two extends Component { public b = 2; }

type CompCon = new (...args: any) => Component;

function receive(one: One, two: Two) { console.log(`one: ${one.a}, two: ${two.b}`) }
function wrongReceive(a: string, b: number) { console.log(`a: ${a}, b: ${b}`) }

function example<T extends Array<CompCon>>(
  callback: (...args: ???) => void,
  ...constructors: T
): void {
  let instances = constructors.map( (c: CompCon) => new c() );
  callback(...instances);
}

example(receive, One, Two); // should be ok
example(wrongReceive, One, Two); // should have typescript compile errors on wrongReceive having wrong signature
example((c: One, d: Two) => {  // should be ok
  console.log(`c: ${c.a}, d: ${d.b}`);
});

1 个答案:

答案 0 :(得分:1)

您å¯ä»¥è¿™æ ·åšã€‚定义Ctor:

type Ctor<C> = new (...args: any[]) => C;

Ctor<C>是一ç§â€œå¯ä»¥ç”¨new调用并构造C的东西â€çš„类型

然åŽæ‚¨å¯ä»¥å®šä¹‰CtorsOf:

type CtorsOf<T> = { [K in keyof T]: Ctor<T[K]> };

如果T是类型的元组,则CtorsOf<T>会生æˆä¸€ä¸ªå…ƒç»„,该元组期望æ¯ç§T类型的构造函数。例如。 CtorsOf<[One, Two]>将解æžä¸º[Ctor<One>, Ctor<Two>]。

然åŽæ‚¨å¯ä»¥åƒè¿™æ ·å®šä¹‰example:

function example<C extends Component[]>(
  callback: (...args: C) => void,
  ...constructors: CtorsOf<C>
): void {
  let instances = constructors.map(c => new c()) as C;
  callback(...instances);
}

C是一个元组,定义了您希望在回调中使用的å‚数的类型,然åŽä»Žè¯¥å…ƒç»„中派生出...constructorså‚数的构造函数的元组。

我没有办法é¿å…let instances = ... as C中的类型声明。那里的问题是constructors的元组会通过.mapæ“作丢失,并且结果数组的类型为Component[]。我å°è¯•äº†ä¸€äº›å˜ä½“,但是å³ä½¿ä½¿ç”¨äº†([1, "2"] as [number, string]).map(x => x);这样的ç碎内容,原始数组的元组也会丢失,TS会为生æˆçš„(string | number)[]数组推断出最终类型。

以下是从您的原始æ¥æºæ”¹ç¼–而æˆçš„完整示例:

class Component {}
class One extends Component { public a = 1; }
class Two extends Component { public b = 2; }

function receive(one: One, two: Two) { console.log(`one: ${one.a}, two: ${two.b}`) }
function wrongReceive(a: string, b: number) { console.log(`a: ${a}, b: ${b}`) }

// Ctor<C> is a type for "something which can be called with new and constructs a C"
type Ctor<C> = new (...args: any[]) => C;

// Given T a tuple of types, CtorsOf<T> produces a tuple which expects a constructor
// for each type of T. Eg. CtorsOf<[One, Two]> would be [Ctor<One>, Ctor<Two>]
type CtorsOf<T> = { [K in keyof T]: Ctor<T[K]> };

// If you uncomment this and use an editor that shows you what Q expands to, you'll see that
// it expands to [Ctor<One>, Ctor<Two>].
// type Q = CtorsOf<[One, Two]>;

function example<C extends Component[]>(
  callback: (...args: C) => void,
  ...constructors: CtorsOf<C>
): void {
  let instances = constructors.map(c => new c()) as C;
  callback(...instances);
}

example(receive, One, Two); // This is okay.
example((c: One, d: Two) => {  // This is okay.
  console.log(`c: ${c.a}, d: ${d.b}`);
}, One, Two);
// example(wrongReceive, One, Two); // Fails to compile.
// example(receive, Two, One); // Fails to compile.

class TwoPrime extends Two { public bPrime = 1; };

example(receive, One, TwoPrime); // This is okay too. TwoPrime is a subclass of Two.

function receivePrime(one: One, two: TwoPrime) { console.log(`one: ${one.a}, two: ${two.b}`) }

example(receivePrime, One, TwoPrime); // This is okay.
// example(receivePrime, One, Two); // Fails to compile. The shape of Two is not compatible with TwoPrime.

const z = ([1, "2"] as [number, string]).map(x => x);