首页 > 解决方案 > TypeScript 中具有泛型的默认参数

问题描述

考虑 TypeScript 文档中的最后一个泛型示例: https ://www.typescriptlang.org/docs/handbook/generics.html#using-class-types-in-generics

如果我想为我将如何执行此操作Lion设置默认值?createInstance()

例如:

class Animal {
  numLegs: number = 0;
}

class Lion extends Animal {
  keeper = "zookeeper";
}

function createInstance<A extends Animal>(c: new () => A = Lion): A { // error!
// -------------------------------------> ~~~~~~~~~~~~~~~~~~~~~
//  'Lion' is assignable to the constraint of type 'A', but 'A' could be 
// instantiated with a different subtype of constraint 'Animal'.
  return new c();
}

游乐场链接

标签: typescript

解决方案


您可以使用泛型参数默认值类型断言来告诉编译器缺乏对A意味着Ais的推断Lion,并且可以做出这样的假设:

function createInstanceAssert<A extends Animal = Lion>(c: new () => A = Lion as any) {
  return new c();
}

我认为这将在您的用例中按需要工作:

createInstanceAssert(Lion).keeper.nametag;
createInstanceAssert(Bee).keeper.hasMask;
createInstanceAssert().keeper.nametag;

虽然有可能有人手动指定泛型参数并造成麻烦:

createInstanceAssert<Bee>().keeper.hasMask.valueOf(); // compiles, but error at runtime
// ---------------> ~~~~~ don't do this, okay?

这可能不太可能,但您应该意识到这一点。


如果您真的想防止误用,可以使用重载来区分两个单独的用例:

function createInstanceOverload<A extends Animal>(c: new () => A): A;
function createInstanceOverload(): Lion;
function createInstanceOverload(c: new () => Animal = Lion) {
  return new c();
}

您基本上可以使用参数调用它,在这种情况下它是通用的并且A是推断的,或者您可以不带参数调用它,在这种情况下它不是通用的,并且Lion出现:

createInstanceOverload(Lion).keeper.nametag;
createInstanceOverload(Bee).keeper.hasMask;
createInstanceOverload().keeper.nametag;

由于没有通用的零参数调用签名,因此在没有大的编译器错误的情况下不再可能进行以下操作:

createInstanceOverload<Bee>().keeper.hasMask.valueOf(); // error at compile time

Playground 代码链接


推荐阅读