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_Constructor Overloading_Type Narrowing_Typeguards - Fatal编程技术网

如何缩小TypeScript中构造的泛型类?

如何缩小TypeScript中构造的泛型类?,typescript,generics,constructor-overloading,type-narrowing,typeguards,Typescript,Generics,Constructor Overloading,Type Narrowing,Typeguards,我有一个类方法,让我们调用它continue,它接受回调。该方法返回的值类型与给定回调返回的值类型相同。到目前为止,很简单: function continue<T>(callback: () => T): T { // ... } 现在,语义可以属于一组有限的可能类型;我需要根据所使用的检查点的风格来区分运行时的行为: type runtimeDiscriminator = 'Script' | 'Statement' class Checkpoint<Sem

我有一个类方法,让我们调用它continue,它接受回调。该方法返回的值类型与给定回调返回的值类型相同。到目前为止,很简单:

function continue<T>(callback: () => T): T {
   // ...
}
现在,语义可以属于一组有限的可能类型;我需要根据所使用的检查点的风格来区分运行时的行为:

type runtimeDiscriminator = 'Script' | 'Statement'

class Checkpoint<Semantic> {
   type: runtimeDiscriminator

   constructor(blah: any, type: runtimeDiscriminator) {
      // ...
      this.type = type
   }

   continue<T>(callback: (val: Semantic) => T): T {
      if (this.type === 'Script') { /* ... do things */ }
      else { /* ... do other things */ }
   }
}
不幸的是,我从typechecker中得到以下错误:

类型批注不能出现在构造函数声明上。ts1093

我不理解这个限制,也不知道在这种情况下如何解决它:我需要关于不透明值类型的运行时信息;但我不想对每个调用站点进行两次注释,即新的检查点“语句”

如何像这样重载构造函数


编辑:我希望我没有弄乱这里的任何打字机术语;我属于OCaml血统,所以有时我会被C++风格的TypeScript术语淹没x

关于TypeScript中命名约定的注意事项:泛型类型参数名称通常只有一个或两个大写字符,尽管它缺乏表达能力。类型别名和接口的名称几乎总是以大写字母开头,而非构造函数值的名称几乎总是以小写字母开头。在这里我将遵循这些惯例

我认为最好的方法是创建一个从discriminator RuntimeDiscriminator到discriminated Semantic类型的映射,并使您的检查点类在discriminator本身中通用。比如:

interface SemanticMap {
  Script: Script;
  Statement: Statement;
}

type RuntimeDiscriminator = keyof SemanticMap;
请注意,代码中不必有SemanticMap接口的单个实例;这只是为了帮助类型系统理解字符串文字名称和类型之间的关系,接口非常适合将字符串文字名称映射到类型

class Checkpoint<K extends RuntimeDiscriminator> {
  type: K;

  constructor(blah: any, type: K) {
    this.type = type;
  }

  producesScript(): this is Checkpoint<"Script"> {
    return this.type === "Script";
  }

  producesStatement(): this is Checkpoint<"Statement"> {
    return this.type === "Statement";
  }
您可能会发现自己需要在continue的实现中使用或类似的东西,因为编译器通常不喜欢为泛型类型指定具体的值;它无法验证这些是安全的,也没有真正尝试,请参阅。即使在代码中也是如此;使用SemanticMap[K]代替SemanticMap[K]并不是一个特别的限制

让我们验证它的行为是否符合您的要求:

function scriptAcceptor(s: Script): string {
  return "yummy script";
}

function statementAcceptor(s: Statement): string {
  return "mmmm statement";
}

const scriptCheckpoint = new Checkpoint(12345, "Script"); // Checkpoint<"Script">
const scrVal = scriptCheckpoint.continue(scriptAcceptor); // string

const statementCheckpoint = new Checkpoint(67890, "Statement"); // Checkpoint<"Statement">
const staVal = statementCheckpoint.continue(statementAcceptor); // string

const oops = scriptCheckpoint.continue(statementAcceptor); // error!
//                                     ~~~~~~~~~~~~~~~~~
// Argument of type '(s: Statement) => string' is not assignable
// to parameter of type '(val: Script) => string'.
这对我来说是正确的

另一方面,我认为如果您决定以调用这些类型保护并打开结果的方式实现continue方法,相反,您可以考虑将检查点设为抽象的超类,并具有具体的Script检查点扩展检查点和StestMekStPoT扩展检查点子类,每个子类实现它们自己的继续方法。你会用新的ScriptCheckpointblah替换新的Checkpointblah脚本。这减轻了检查站必须像两个不同事物一样行事的负担。我不知道你的用例,所以我不会在这个方向上走得更远;这只是一个值得考虑的问题。
一种解决方案是使用重载静态工厂方法,并使构造函数私有。这是一个选项还是必须是构造函数?这显然是我的备份!我真的希望这个API的JavaScript端尽可能地惯用,因此,是的,我更希望找到一种方法,从调用构造函数的方式正确推断类型。还有,学习锻炼!我理解。我想我找到了一些有效的方法。。。只需再花几分钟记下细节,然后发布答案。
class Checkpoint<K extends RuntimeDiscriminator> {
  type: K;

  constructor(blah: any, type: K) {
    this.type = type;
  }

  producesScript(): this is Checkpoint<"Script"> {
    return this.type === "Script";
  }

  producesStatement(): this is Checkpoint<"Statement"> {
    return this.type === "Statement";
  }
  continue<T>(callback: (val: SemanticMap[K]) => T): T {
    return callback(getSemanticInstance(this.type)); // or something
  }

}
function scriptAcceptor(s: Script): string {
  return "yummy script";
}

function statementAcceptor(s: Statement): string {
  return "mmmm statement";
}

const scriptCheckpoint = new Checkpoint(12345, "Script"); // Checkpoint<"Script">
const scrVal = scriptCheckpoint.continue(scriptAcceptor); // string

const statementCheckpoint = new Checkpoint(67890, "Statement"); // Checkpoint<"Statement">
const staVal = statementCheckpoint.continue(statementAcceptor); // string

const oops = scriptCheckpoint.continue(statementAcceptor); // error!
//                                     ~~~~~~~~~~~~~~~~~
// Argument of type '(s: Statement) => string' is not assignable
// to parameter of type '(val: Script) => string'.