Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
理解TypeScript中的泛型约束扩展_Typescript_Generics - Fatal编程技术网

理解TypeScript中的泛型约束扩展

理解TypeScript中的泛型约束扩展,typescript,generics,Typescript,Generics,我试图创建一个泛型函数,该函数接受基类的子类型,并返回解析为指定类实例的承诺。此示例显示了我试图实现的目标: class foo {} class bar extends foo {} const someBar = new bar(); function foobar<T extends foo>(): Promise<T> { return new Promise<T>(resolve => resolve(someBar)); // this

我试图创建一个泛型函数,该函数接受基类的子类型,并返回解析为指定类实例的承诺。此示例显示了我试图实现的目标:

class foo {}
class bar extends foo {}
const someBar = new bar();
function foobar<T extends foo>(): Promise<T> {
  return new Promise<T>(resolve => resolve(someBar)); // this doesn't compile
}
TS正确地推断结果是一个条,但它不允许我在函数中返回someBar

在我的泛型类方法处理
this
的类中,我已经能够使用多态
this
来实现这一点,但我不在类中

更新2

实际上,这不需要承诺来说明我在做什么。在这里,我进一步简化了(并省略了foo和bar的定义,因为这并没有改变):


正如你所看到的,我所做的是微不足道的。我只是尝试在不向下转换的情况下进行多态类型检查。

您提供的代码不是泛型函数,因此您尝试将其作为泛型函数键入时走错了路

泛型函数需要处理各种数据类型,同时仍然能够指定函数不同部分中类型之间的关系。例如,您可以指定函数传递某种类型,然后返回相同类型的数组

function wrapInArray<T> (input: T): T[] {
   return [T];
}
// To be used like:

const result1 = wrapInArray(1234); // result1 is a number array;
const result2 = wrapInArray('hello'); // result2 is a string array;

您的函数只做一件事:返回一个
承诺
。您需要指定的类型之间没有关联,并且没有可能的方法调用函数,这将导致函数返回除
承诺以外的内容。因此,正确的类型是:

const someBar = new bar();
function foobar(): Promise<bar> {
  return new Promise(resolve => resolve(someBar));
}

好的,我非常感谢这里的所有反馈——但我已经对此进行了研究,这里有一点值得一提。首先,我很惊讶有这么多人关注我的例子。我故意提供了一个非常简单的例子。这就是所谓的“最小复制”——我的简单示例显示了一个编译器错误,其中像我这样的人可能(当然是错误的)认为返回类型是受约束泛型类型的有效多态子类型。同样,大多数回答都没有错——但它们没有完全解决这个问题。我会尽量提供一个更完整的图片在这里

我将对我的最后一个示例进行一点细分:

class Foo { f() {} }
class Bar extends Foo { b() {} }
var someBar = new Bar();
function foobar<T extends Foo>(): T {
  return someBar;
}
const mybar: Bar = foobar<Bar>();
这不是按原样编译的。正如在我上面的
return someBar
中一样,TS不记得t在结构上与MdkObjectBase兼容。它的行为就像T可以是任何东西,因此我无法从Global.load(承诺)分配结果

有趣的是,如果_resolve返回GlobalResolver的子类,我可以将其指定为返回类型:
resolve(keyPath:string):this
。这利用了TS中的“多态This”。它不是泛型,但它允许我从基类返回一个子类,而无需调用者向下转换。但是我需要用一种不同的类型来做这个,而且似乎没有办法


总而言之:TypeScript泛型中的类型约束约束函数可以接受的类型。但是它不会影响函数中的泛型类型——TypesScript选择忘记该信息。我相信这将是有用的,就像多态性一样,这有助于防止需要进行降级。而且这也不是史无前例的(正如我所描述的,我非常确定Swift工作中的通用约束)。但是,唉,这不是TS的创造者们所想象的,我相信他们的选择是有原因的。我只能沮丧了。

这是一个关于泛型的常见误解。泛型参数由函数的调用者决定。函数不能决定泛型参数是什么。通过在这里返回
someBar
,您的函数假设
T
bar
,它不一定是这样。我想我不理解约束的作用,如果它不约束类型=),它就是约束类型。不能使用任何
t
调用该方法。泛型约束并不约束方法实现(它实际上允许您在实现中执行更多操作),而是约束调用者。@EricS如果您使用纯javascript编写此函数,代码会是什么?(您给出了一个描述,但它与您提供的代码并不完全匹配)如果我们了解javascript,我们也许可以帮助您向其中添加类型信息。嗨@NicholasTower-我不确定纯javascript在这里会有帮助,因为我只是使用了一个简单的承诺-但尝试实现多态静态类型安全。但我确实编辑了上面的问题,并展示了一个非通用版本。此版本要求我向下转换结果。我在努力避免沮丧。这有意义吗?
它不再将其视为“Foo的子类T”,而是“已知宇宙中的任何类型”
抱歉,但这是错误的。在函数内部,可以使用Foo的任何方法。你可以这样做,因为它肯定是一个Foo,没有更广泛的。但它也可能比foo更窄,所以不能使用任何需要更窄类型的东西(除非先进行类型检查)。因此,试图返回一个条比一个Foo更窄,typescript说“等等,有人可能会用
调用这个函数,在这种情况下,您返回了比他们期望的更窄的值”另一种看待问题的方式:有人可以用T=Qux调用函数。Qux从Foo扩展而来,它有一个Foo没有的额外属性(Bar也没有)。您列出的类型指定,由于T=Qux,将返回一个Qux。(或一个
承诺
,取决于我们使用的示例)。但是您的代码无法保证额外属性是返回对象的一部分。您将返回Bar上的所有额外属性,但这对使用Qux调用它的人没有帮助。
它应该很高兴地允许我这样做
function foobar<T extends Foo>(): T {
  return someBar;
}
const mybar: Bar = foobar<Bar>();
var someBar = new bar();
function foobar() {
  return someBar;
}
var myBar = foobar();
function wrapInArray<T> (input: T): T[] {
   return [T];
}
// To be used like:

const result1 = wrapInArray(1234); // result1 is a number array;
const result2 = wrapInArray('hello'); // result2 is a string array;
function getLength<T extend { length: number }>(input: T): number {
  // I can access input.length because even though i don't know what input's type is,
  //   i do know that it's illegal to call this function with anything that doesn't
  //   have a length property.
  return input.length;
}

// to be used like:

const len1 = getLength([1, 2, 3]);
const len2 = getLength({ length: 5 });
const someBar = new bar();
function foobar(): Promise<bar> {
  return new Promise(resolve => resolve(someBar));
}
function foobar<T extends Foo>(): T {
  return someBar;
}
const mybar: Bar = foobar<Bar>();
function foobar(): Bar {
  return someBar;
}


const myBar = foobar(); 
// You could add the type explicitly if you prefer, but it's not necessary:
// const myBar: Bar = foobar();
// And since anything that's a Bar necessarily has all the properties of a Foo, you can also do:
const myFoo: Foo = foobar();

class Foo { f() {} }
class Bar extends Foo { b() {} }
var someBar = new Bar();
function foobar<T extends Foo>(): T {
  return someBar;
}
const mybar: Bar = foobar<Bar>();
class GlobalResolver extends CachingResolver {
  protected _resolve<T extends MdkObjectBase>(
    keyPath: string[]
  ): Promise<T> {
    const globName = keyPath.join("/");
    return Global.load(this._app, this._loadContext, `./Globals/${globName}`);
  }
}