范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

TypeScript5。0Beta来了

  今天,我们很高兴地宣布 TypeScript 5.0 的测试版发布!
  此版本带来了许多新功能,同时旨在使 TypeScript 更小、更简单、更快。 我们已经实现了新的装饰器标准、更好地支持 Node 和捆绑器中的 ESM 项目的功能、库作者控制泛型推理的新方法、扩展了我们的 JSDoc 功能、简化了配置,并进行了许多其他改进。
  虽然 5.0 版本包括正确性更改和对较少使用的标志的弃用,但我们相信大多数用户将拥有与以前版本类似的升级体验。
  要开始使用测试版,您可以通过 NuGet 获取它,或通过以下命令使用 npm: npm install typescript@beta装饰器
  装饰器是即将推出的 ECMAScript 功能,它允许我们以可重用的方式自定义类及其成员。
  让我们考虑以下代码: class Person {     name: string;     constructor(name: string) {         this.name = name;     }      greet() {         console.log(`Hello, my name is ${this.name}.`);     } }  const p = new Person("Ray"); p.greet();
  greet 在这里非常简单,但让我们想象它是更复杂的东西——也许它执行一些异步逻辑,它是递归的,它有副作用等等。不管你想象的是哪种,假设你调用console.log输出一些以帮助调试问候语。class Person {     name: string;     constructor(name: string) {         this.name = name;     }      greet() {         console.log("LOG: Entering method.");          console.log(`Hello, my name is ${this.name}.`);          console.log("LOG: Exiting method.")     } }
  这种模式相当常见。如果有一种方法可以让我们对每个方法都这样做,那就太好了!
  这就是装饰器的作用。我们可以编写一个名为loggedMethod的函数,如下所示: function loggedMethod(originalMethod: any, _context: any) {      function replacementMethod(this: any, ...args: any[]) {         console.log("LOG: Entering method.")         const result = originalMethod.call(this, ...args);         console.log("LOG: Exiting method.")         return result;     }      return replacementMethod; }
  "这些any是怎么回事?"
  耐心点——我们现在让事情变得简单,这样我们就可以专注于这个函数在做什么。注意,loggedMethod接受原始方法(originalMethod)并返回一个函数 记录一条"输入(Entering)……"的消息将this及其所有参数传递给原始方法记录"退出(Exiting)……"消息返回原始方法返回的值
  现在我们可以使用loggedMethod来装饰greet: class Person {     name: string;     constructor(name: string) {         this.name = name;     }      @loggedMethod     greet() {         console.log(`Hello, my name is ${this.name}.`);     } }  const p = new Person("Ray"); p.greet();  // Output: // //   LOG: Entering method. //   Hello, my name is Ray. //   LOG: Exiting method.
  我们只是在greet上面使用了loggedMethod作为装饰器——注意,我们把它写成了@loggedMethod。当我们这样做时,它会被target方法和一个context对象调用。因为loggedMethod返回了一个新函数,所以这个函数替换了greet的原始定义。
  loggedMethod定义了第二个参数。它被称为"上下文对象",它有一些关于修饰方法是如何声明的有用信息——比如它是#private成员或者是静态的,或者方法的名称是什么。让我们重写loggedMethod来利用这一点,并打印出被装饰的方法的名称。 function loggedMethod(originalMethod: any, context: ClassMethodDecoratorContext) {     const methodName = String(context.name);      function replacementMethod(this: any, ...args: any[]) {         console.log(`LOG: Entering method "${methodName}".`)         const result = originalMethod.call(this, ...args);         console.log(`LOG: Exiting method "${methodName}".`)         return result;     }      return replacementMethod; }
  我们现在使用context参数——它是loggedMethod中第一个类型比any和any[]更严格的参数。TypeScript提供了一个名为ClassMethodDecoratorContext的类型,它为方法装饰器接受的上下文对象建模。
  除了元数据,方法的上下文对象还有一个有用的函数addInitializer。这是一种挂接到构造函数开头的方式(如果使用静态方法,则挂接到类本身的初始化)。
  举个例子——在JavaScript中,经常会写如下的模式: class Person {     name: string;     constructor(name: string) {         this.name = name;          this.greet = this.greet.bind(this);     }      greet() {         console.log(`Hello, my name is ${this.name}.`);     } }
  或者,可以将greet声明为一个初始化为箭头函数。 class Person {     name: string;     constructor(name: string) {         this.name = name;     }      greet = () => {         console.log(`Hello, my name is ${this.name}.`);     }; }
  编写这段代码是为了确保当greet作为独立函数调用或作为回调函数传递时,不会重新绑定。 const greet = new Person("Ray").greet;  // 我们不想这里出错 greet();
  我们可以编写一个装饰器,使用addInitializer在构造函数中为我们调用bind。 function bound(originalMethod: any, context: ClassMethodDecoratorContext) {     const methodName = context.name;     if (context.private) {         throw new Error(`"bound" cannot decorate private properties like ${methodName as string}.`);     }     context.addInitializer(function () {         this[methodName] = this[methodName].bind(this);     }); }
  当Bound装饰一个方法时它不会返回任何东西,它会保留原来的方法。相反,它会在其他字段初始化之前添加逻辑。class Person {     name: string;     constructor(name: string) {         this.name = name;     }      @bound     @loggedMethod     greet() {         console.log(`Hello, my name is ${this.name}.`);     } }  const p = new Person("Ray"); const greet = p.greet;  // Works! greet();
  注意,我们用了两个装饰器——@bound和@loggedMethod。这些装饰是以"相反的顺序"运行的。也就是说,@loggedMethod修饰了原始方法greet, @bound修饰了@loggedMethod的结果。在这个例子中,这没有关系——但如果你的装饰器有副作用或期望某种顺序,则需要关注他们的顺序。
  同样值得注意的是——如果你更喜欢代码风格化,你可以将这些装饰器放在同一行。     @bound @loggedMethod greet() {         console.log(`Hello, my name is ${this.name}.`);     }
  可能不太明显的是,我们甚至可以创建返回装饰器函数的函数。这使得我们可以对最终的装饰器进行一些自定义。如果我们愿意,我们可以让loggedMethod返回一个装饰器,并自定义它记录消息的方式。 function loggedMethod(headMessage = "LOG:") {     return function actualDecorator(originalMethod: any, context: ClassMethodDecoratorContext) {         const methodName = String(context.name);          function replacementMethod(this: any, ...args: any[]) {             console.log(`${headMessage} Entering method "${methodName}".`)             const result = originalMethod.call(this, ...args);             console.log(`${headMessage} Exiting method "${methodName}".`)             return result;         }          return replacementMethod;     } }
  如果我们这样做,我们必须在使用loggedMethod作为装饰器之前调用它。然后,我们可以传入任何字符串作为输出到控制台的消息的前缀。 class Person {     name: string;     constructor(name: string) {         this.name = name;     }      @loggedMethod("")     greet() {         console.log(`Hello, my name is ${this.name}.`);     } }  const p = new Person("Ray"); p.greet();  // Output: // //    Entering method "greet". //   Hello, my name is Ray. //    Exiting method "greet".
  装饰器可不仅仅用于方法!它们可以用于属性/字段、getter、setter和自动访问器。甚至类本身也可以装饰成子类化和注册。
  有关所涉及更改的更多信息,您可以查看原始的pull request。 与实验性遗留装饰器的差异
  如果你已经使用TypeScript一段时间了,你可能会意识到它多年来一直支持"实验性"装饰器。虽然这些实验性装饰器非常有用,但它们模拟了装饰器提案的一个更旧的版本,并且总是需要一个称为——experimentalDecorators的可选编译器标志。任何在TypeScript中不使用此标志的尝试都会提示错误。
  实验性装饰器将在可预见的未来继续存在;但是,如果没有这个标志,装饰器将成为所有新代码的有效语法。在——experimentalDecorators之外,它们将被类型检查并以不同的方式发出。类型检查规则和emit有很大的不同,虽然可以编写装饰器来同时支持新旧装饰器行为,但任何现有的装饰器函数都不太可能这样做。
  这个新的装饰器提案与——emitDecoratorMetadata不兼容,而且它不允许装饰参数。未来的ECMAScript提案可能有助于弥合这一差距。
  最后说一句:目前,装饰器的提案要求类装饰器在export关键字之后(如果存在的话)。 export @register class Foo {     // ... }  export @Component({     // ... }) class Bar {     // ... }
  TypeScript会在JavaScript文件中强制执行此限制,但TypeScript文件不会这样做。这部分是由现有用户驱动的——我们希望在我们原始的"实验性"装饰器和标准化装饰器之间提供一个稍微简单的迁移路径。此外,我们从许多用户那里听到了对原始样式的偏好,我们希望在未来的标准讨论中真诚地讨论这个问题。 编写类型良好的装饰器
  上面的loggedMethod和bound装饰器示例很简单,并省略了大量关于类型的细节。
  输入装饰器可能相当复杂。例如,上面的loggedMethod类型良好的版本可能看起来像这样: function loggedMethod(     target: (this: This, ...args: Args) => Return,     context: ClassMethodDecoratorContext Return> ) {     const methodName = String(context.name);      function replacementMethod(this: This, ...args: Args): Return {         console.log(`LOG: Entering method "${methodName}".`)         const result = target.call(this, ...args);         console.log(`LOG: Exiting method "${methodName}".`)         return result;     }      return replacementMethod; }
  我们必须使用this、Args和return类型参数分别建模this、参数和原始方法的返回类型。
  具体定义装饰器函数的复杂程度取决于你想要什么。请记住,你的装饰器的使用次数将超过它们的编写次数,所以类型良好的版本通常是更好的——但显然需要与可读性权衡,所以请尽量保持简单。
  constType参数
  在推断对象的类型时,TypeScript通常会选择一种通用的类型。例如,在这个例子中,names的推断类型是string[]: type HasNames = { readonly names: string[] }; function getNamesExactly(arg: T): T["names"] {     return arg.names; }  // Inferred type: string[] const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"]});
  通常这样做的目的是实现突变。
  然而,根据getnames确切的作用以及它的使用方式,通常情况下需要更具体的类型。
  到目前为止,API作者通常不得不建议在某些地方添加const,以实现所需的推断: // 希望类型是: //    readonly ["Alice", "Bob", "Eve"] // 但类型变成: //    string[] const names1 = getNamesExactly({ names: ["Alice", "Bob", "Eve"]});  //正确获得想要的类型: //    readonly ["Alice", "Bob", "Eve"] const names2 = getNamesExactly({ names: ["Alice", "Bob", "Eve"]} as const);
  这可能很麻烦,也很容易忘记。在TypeScript 5.0中,你现在可以在类型参数声明中添加const修饰符,以使const类推断成为默认值: type HasNames = { names: readonly string[] }; function getNamesExactly(arg: T): T["names"] { //                       ^^^^^     return arg.names; }  // Inferred type: readonly ["Alice", "Bob", "Eve"] // Note: Didn"t need to write "as const" here const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
  注意,const修饰符并不排斥可变值,也不需要不可变约束。使用可变类型约束可能会得到令人惊讶的结果。例如: declare function fnBad(args: T): void;  // "T" 仍旧是 "string[]" 因为 "readonly ["a", "b", "c"]" 没有赋值为 "string[]" fnBad(["a", "b" ,"c"]);
  这里,T的推断候选值是readonly ["a", "b", "c"],而readonly数组不能用于需要可变数组的地方。在这种情况下,推理回退到约束,数组被视为string[],调用仍然成功进行。
  更好的定义应该使用readonly string[]: declare function fnGood(args: T): void;  // T is readonly ["a", "b", "c"] fnGood(["a", "b" ,"c"]);
  同样,要记住,const修饰符只影响在调用中编写的对象、数组和基本类型表达式的推断,所以不会(或不能)用const修饰的参数不会看到任何行为的变化: declare function fnGood(args: T): void; const arr = ["a", "b" ,"c"];  // "T" is still "string[]"-- the "const" modifier has no effect here fnGood(arr);
  支持多个配置文件
  当管理多个项目时,从"基础"配置文件tsconfig.Json继承会很有帮助。使用extends字段,用于从compilerOptions中复制字段。 // packages/front-end/src/tsconfig.json {     "compilerOptions": {         "extends": "../../../tsconfig.base.json",          "outDir": "../lib",         // ...     } }
  然而,在某些情况下,您可能希望从多个配置文件扩展。例如,想象一下使用npm中的TypeScript基础配置文件。如果你希望所有项目都使用npm上@tsconfig/strictest包中的选项,那么有一个简单的解决方案:从@tsconfig/strictest扩展tsconfig.base.json: // tsconfig.base.json {     "compilerOptions": {         "extends": "@tsconfig/strictest/tsconfig.json",          // ...     } }
  这在一定程度上是可行的。如果有项目不想使用@tsconfig/strictest,就必须手动禁用该选项,或者创建一个不扩展@tsconfig/strictest的单独版本的tsconfig.base.json。
  为了在这里提供更多的灵活性,Typescript 5.0现在允许extends字段接收多个条目。例如,在这个配置文件中: {     "compilerOptions": {         "extends": ["a", "b", "c"]     } }
  这样写有点像直接扩展c,其中c扩展b, b扩展a。如果任何字段"冲突",则后一项获胜。
  因此,在下面的例子中,strictNullChecks和noImplicitAny都在最终的tsconfig.json文件中启用了。 // tsconfig1.json {     "compilerOptions": {         "strictNullChecks": true     } }  // tsconfig2.json {     "compilerOptions": {         "noImplicitAny": true     } }  // tsconfig.json {     "compilerOptions": {         "extends": ["./tsconfig1.json", "./tsconfig2.json"]     },     "files": ["./index.ts"] }
  作为另一个例子,我们可以用下面的方式重写原来的。 // packages/front-end/src/tsconfig.json {     "compilerOptions": {         "extends": ["@tsconfig/strictest/tsconfig.json", "../../../tsconfig.base.json"],          "outDir": "../lib",         // ...     } }
  有关更多细节,请阅读原始pull request的更多信息。 所有枚举都是Union枚举
  当TypeScript最初引入枚举时,它们只不过是一组具有相同类型的数值常量。 enum E {     Foo = 10,     Bar = 20, }
  E.Foo和E.Bar唯一的特别之处在于它们可以赋值给任何期望类型为E的东西。除此之外,它们基本上都是数字。 function takeValue(e: E) {}  takeValue(E.Foo); // works takeValue(123);   // error!
  直到TypeScript 2.0引入了枚举字面量类型,枚举才变得更加特殊。Enum字面量类型为每个枚举成员指定了自己的类型,并将枚举本身转换为每个成员类型的并集。它们还允许我们只引用枚举类型的一个子集,并缩小这些类型的范围。  enum Color {     Red, Orange, Yellow, Green, Blue, /* Indigo */, Violet }  // 每个枚举成员都有自己的类型,可以引用! type PrimaryColor = Color.Red | Color.Green | Color.Blue;  function isPrimaryColor(c: Color): C is PrimaryColor { //缩小字面量类型可以捕获bug // TypeScript在这里会报错,因为 //我们最终会比较` Color. Color `。从` Red `到` Color.Green `。 //我们本想使用||,但不小心写了&&     return c === Color.Red && c === Color.Green && c === Color.Blue; }
  给每个枚举成员指定自己的类型有一个问题,即这些类型在某种程度上与成员的实际值相关联。在某些情况下,这个值是不可能计算出来的——例如,枚举成员可以通过函数调用进行初始化。 enum E {     Blah = Math.random() }
  每当TypeScript遇到这些问题时,它都会悄无声息地退出并使用旧的枚举策略。这意味着要放弃并集和字面量类型的所有优点。
  TypeScript 5.0通过为每个计算成员创建唯一的类型,设法将所有枚举转换为联合枚举。这意味着现在可以缩小所有枚举的范围,并将其成员作为类型引用。
  有关此更改的更多详细信息,请参阅GitHub上的详细说明。 ——moduleResolution打包
  TypeScript 4.7为其——module和——modulerresolution设置引入了node16和nodenext选项。这些选项的目的是为了更好地在Node.js中为ECMAScript模块建立精确的查找规则模型。然而,这种模式有许多其他工具没有真正实施的限制。
  例如,在Node.js的ECMAScript模块中,任何相对导入都需要包含文件扩展名。 // entry.mjs import * as utils from "./utils";     //  wrong - 需要包含文件扩展名  import * as utils from "./utils.mjs"; //  works
  在Node.js和浏览器中这样做是有原因的——它使文件查找更快,并且对于简单的文件服务器工作得更好。但对于许多使用打包工具的开发人员来说,node16/nodenext的设置很麻烦,因为打包工具没有这些限制。在某种程度上,node解析模式更适合任何使用打包工具的人。
  但在某些方面,最初的node解析模式已经过时了。大多数现代打包工具融合了Node.js中的ECMAScript模块和CommonJS查找规则。例如,无扩展导入就像在CommonJS中一样正常工作,但当查看包的导出条件时,他们会更喜欢ECMAScript文件中的导入条件。
  为了建模打包的工作方式,TypeScript现在引入了一个新的策略:——modulerresolution打包。 {     "compilerOptions": {         "target": "esnext",         "moduleResolution": "bundler"     } }
  如果你正在使用现代的打包工具,比如Vite、esbuild、swc、Webpack、Parcel,以及其他实现了混合查找策略的工具,那么新的打包工具选项应该很适合你。
  要了解有关——modulerresolution打包工具的更多信息,请查看实现的拉取请求。 解析自定义标志
  JavaScript工具现在可以对"混合"解析规则建模,就像我们上面描述的打包模式一样。由于工具的支持可能略有不同,TypeScript 5.0提供了启用或禁用一些功能的方法,这些功能可能与您的配置一起使用,也可能无法使用。 allowImportingTsExtensions
  --allowImportingTsExtensions  允许TypeScript文件使用特定于TypeScript的扩展名(如.ts、.mts或.tsx)相互导入。
  仅当启用——noEmit或——emitDeclarationOnly时,才允许使用此标志,因为这些导入路径在运行时无法在JavaScript输出文件中解析。这里的期望是你的解析器(例如打包、运行时或其他工具)将使这些在.ts文件之间的导入正常工作。resolvePackageJsonExports
  ——resolvePackageJsonExports强制TypeScript查询package的exports字段。如果它从node_modules中的包中读取数据,则会读取Json文件。
  在——moduleResolution的node16、nodenext和bundler选项中,这个选项默认为true。resolvePackageJsonImports
  ——resolvePackageJsonImports 强制TypeScript查询package的imports字段。当查找以#开头的文件时,该文件的祖先目录包含package.json文件。
  在——moduleResolution的node16、nodenext和bundler选项中,这个选项默认为true。allowArbitraryExtensions
  在TypeScript 5.0中,当导入路径以一个不是已知的JavaScript或TypeScript文件扩展名的扩展名结束时,编译器将查找该路径形式为{file basename}.d.{extension}.ts的声明文件。例如,如果你在打包项目中使用CSS loader,你可能想为这些样式表编写(或生成)声明文件: /* app.css */ .cookie-banner {   display: none; } // app.d.css.ts declare const css: {   cookieBanner: string; }; export default css; // App.tsx import styles from "./app.css";  styles.cookieBanner; // string
  默认情况下,这个导入将引发一个错误,让你知道TypeScript不理解这个文件类型,你的运行时可能不支持导入它。但是,如果您已经配置了运行时或打包器来处理它,则可以使用新的——allowArbitraryExtensions编译器选项来抑制错误。
  请注意,在历史上,通常通过添加一个名为app.css.d.ts的声明文件而不是app.d.css.ts来实现类似的效果——然而,这只是通过Node对CommonJS的require解析规则实现的。严格来说,前者是一个名为app.css.js的JavaScript文件的声明文件。因为相对文件导入需要包含Node对ESM支持的扩展,所以在我们的例子中,TypeScript会在——moduleResolution node16或nodenext下的ESM文件中出错。
  有关更多信息,请阅读此功能的建议及其相应的pull request。 customConditions
  ——customConditions 接受额外的条件列表,当TypeScript从package.json的[exports]或(https://nodejs.org/api/packages.html#exports)或imports字段解析时,这些条件应该成功。这些条件将添加到解析器默认使用的任何现有条件中。
  例如,当在tsconfig. conf中设置此字段时。Json格式如下:{     "compilerOptions": {         "target": "es2022",         "moduleResolution": "bundler",         "customConditions": ["my-condition"]     } }
  在包中引用exports或imports字段时。TypeScript将考虑my-condition条件。
  因此,当使用以下package.json从一个包中导入时 {     // ...     "exports": {         ".": {             "my-condition": "./foo.mjs",             "node": "./bar.mjs",             "import": "./baz.mjs",             "require": "./biz.mjs"         }     } }
  TypeScript将尝试查找与foo.mjs对应的文件。
  这个字段只有在node16、nodenext和bundler选项下——modulerresolution才有效 --verbatimModuleSyntax
  默认情况下,TypeScript会执行导入省略(import elision)操作。基本上,如果你写 import { Car } from "./car";  export function drive(car: Car) {     // ... }
  TypeScript检测到你只使用了类型导入,并完全删除导入。输出的JavaScript代码可能类似于下面这样: export function drive(car) {     // ... }
  大多数情况下,这是很好的,因为如果Car不是从./ Car导出的值,我们会得到一个运行时错误。
  但对于某些边界情况,它确实增加了一层复杂性。例如,没有import "./car"这样的语句;-完全放弃导入。这实际上对有没有副作用的模块有影响。
  TypeScript的JavaScript emit策略也有另外几层复杂性——省略导入并不总是由如何使用导入驱动的——它通常还会咨询如何声明值。因此,下面的代码是否像下面这样并不总是很清楚 export { Car } from "./car";
  应保存或丢弃。如果Car使用类之类的东西声明,那么它可以保留在生成的JavaScript文件中。但是,如果Car仅被声明为类型别名或接口,那么JavaScript文件根本不应该导出Car。
  虽然TypeScript可能能够根据来自跨文件的信息做出这些发送决策,但不是每个编译器都可以。
  导入和导出的类型修饰符在这些情况下有一点帮助。我们可以明确指定import或export仅用于类型分析,并且可以在JavaScript文件中使用类型修饰符完全删除。 //这条语句可以在JS输出中完全删除 import type * as car from "./car";  // 在JS输出中可以去掉命名的import/export ` Car ` import { type Car } from "./car"; export { type Car } from "./car";
  类型修饰符 本身并不是很有用——默认情况下,模块省略仍然会删除导入,并且没有任何东西迫使你区分类型和普通的导入和导出。因此,TypeScript有标志——importsNotUsedAsValues以确保您使用类型修饰符,——preserveValueImports以防止某些模块省略行为,以及——isolatedModules以确保您的TypeScript代码在不同的编译器上工作。不幸的是,理解这3个标志的细节是困难的,并且仍然存在一些具有意外行为的边缘情况。
  TypeScript 5.0引入了一个名为——verbatimModuleSyntax的新选项来简化这种情况。规则要简单得多——没有类型修饰符的任何导入或导出都将保留。任何使用类型修饰符的元素都被完全删除。// 完全抹去 import type { A } from "a";  // 重写 "import { b } from "bcd";" import { b, type c, type d } from "bcd";  // 重写 "import {} from "xyz";" import { type xyz } from "xyz";
  有了这个新选项,所见即所得。
  不过,当涉及到模块互操作时,这确实有一些含义。在这个标志下,当你的设置或文件扩展名意味着需要使用不同的模块系统时,ECMAScript的import和export函数不会被重写。相反,你会得到一个错误。如果你需要发出使用require和module的代码。导出时,你必须使用ES2015之前的TypeScript模块语法:
  虽然这是一个限制,但它确实有助于使一些问题更加明显。例如,在package.json中忘记设置type字段是很常见。因此,开发人员会在不知不觉中开始编写CommonJS模块,而不是ES模块,给出令人惊讶的查找规则和JavaScript输出。这个新标志确保你有意使用不同的文件类型,因为它们的语法是不同的。
  因为——verbatimModuleSyntax比——importsNotUsedAsValues和——preserveValueImports提供了一个更一致的事实,所以这两个现有的标志被弃用了。 支持foreexport类型*
  当TypeScript 3.8引入纯类型导入时,新语法不允许从"module"导出*或从"module"重新导出ns时导出。TypeScript 5.0增加了对这两种形式的支持: // models/vehicles.ts export class Spaceship {   // ... }  // models/index.ts export type * as vehicles from "./spaceship";  // main.ts import { vehicles } from "./models";  function takeASpaceship(s: vehicles.Spaceship) {   //  ok -`vehicles`只在type位置使用 }  function makeASpaceship() {   return new vehicles.Spaceship();   //         ^^^^^^^^   // ` vehicles `不能用作值,因为它是使用` export type `导出的。 }
  JSDoc中的@satisfiesSupport
  TypeScript 4.9引入了satisfaction操作符。它确保表达式的类型是兼容的,而不会影响类型本身。例如,让我们看看下面的代码: interface CompilerOptions {     strict?: boolean;     outDir?: string;     // ...      extends?: string | string[]; }  declare function resolveConfig(configPath: string): CompilerOptions;  let myCompilerOptions = {     strict: true,     outDir: "../lib",     // ...      extends: [         "@tsconfig/strictest/tsconfig.json",         "../../../tsconfig.base.json"     ],  } satisfies CompilerOptions;
  在这里,TypeScript知道myCompilerOptions.extends是用数组声明的——因为while满足验证了对象的类型,它不会直接将其更改为CompilerOptions并丢失信息。如果我们想映射到extends,没问题。 let inheritedConfigs = myCompilerOptions.extends.map(resolveConfig);
  这对TypeScript用户很有帮助,但是很多人使用TypeScript使用JSDoc注释对JavaScript代码进行类型检查。这就是为什么TypeScript 5.0支持一个名为@ satisfy的新JSDoc标签,它做的事情完全相同。
  /** @ satisfy */可以捕获类型不匹配: // @ts-check  /**  * @typedef CompilerOptions  * @prop {boolean} [strict]  * @prop {string} [outDir]  * @prop {string | string[]} [extends]  */  /**  * @satisfies {CompilerOptions}  */ let myCompilerOptions = {     outdir: "../lib", //  ~~~~~~ oops! we meant outDir };
  但它将保留表达式的原始类型,允许我们在后面的代码中更精确地使用值。 // @ts-check  /**  * @typedef CompilerOptions  * @prop {boolean} [strict]  * @prop {string} [outDir]  * @prop {string | string[]} [extends]  */  /**  * @satisfies {CompilerOptions}  */ let myCompilerOptions = {     strict: true,     outDir: "../lib",     extends: [         "@tsconfig/strictest/tsconfig.json",         "../../../tsconfig.base.json"     ], };  let inheritedConfigs = myCompilerOptions.extends.map(resolveConfig);
  /** @satisfies */  也可以在任何括号表达式中使用。我们可以这样写myCompilerOptions:let myCompilerOptions = /** @satisfies {CompilerOptions} */ ({     strict: true,     outDir: "../lib",     extends: [         "@tsconfig/strictest/tsconfig.json",         "../../../tsconfig.base.json"     ], });
  为什么?好吧,当你深入到其他代码中时,比如函数调用,它通常更有意义。 compileCode(/** @satisfies {CompilerOptions} */ ({     // ... }));
  此功能由Oleksandr Tarasiuk提供! JSDoc中的@overloadSupport
  在TypeScript中,你可以为函数指定重载。重载为我们提供了一种方式,可以使用不同的参数调用函数,并可能返回不同的结果。它们可以限制调用者实际可以如何使用我们的函数,并细化他们将返回的结果。 // Our overloads: function printValue(str: string): void; function printValue(num: number, maxFractionDigits?: number): void;  // Our implementation: function printValue(value: string | number, maximumFractionDigits?: number) {     if (typeof value === "number") {         const formatter = Intl.NumberFormat("en-US", {             maximumFractionDigits,         });         value = formatter.format(value);     }      console.log(value); }
  这里我们说过,printValue的第一个参数要么是字符串,要么是数字。如果它接收一个数字,那么它可以接收第二个实参来确定我们可以打印多少个小数。
  TypeScript 5.0现在允许JSDoc使用新的@overload标签来声明重载。每个带有@overload标签的JSDoc注释都被视为下面函数声明的不同重载。 // @ts-check  /**  * @overload  * @param {string} value  * @return {void}  */  /**  * @overload  * @param {number} value  * @param {number} [maximumFractionDigits]  * @return {void}  */  /**  * @param {string | number} value  * @param {number} [maximumFractionDigits]  */ function printValue(value, maximumFractionDigits) {     if (typeof value === "number") {         const formatter = Intl.NumberFormat("en-US", {             maximumFractionDigits,         });         value = formatter.format(value);     }      console.log(value); }
  现在,无论我们是使用TypeScript还是JavaScript文件编写,TypeScript都可以让我们知道我们是否错误地调用了函数。 // all allowed printValue("hello!"); printValue(123.45); printValue(123.45, 2);  printValue("hello!", 123); // error!
  在—build下传递特定的标志
  TypeScript现在允许在——build模式下传递以下标志 --declaration --emitDeclarationOnly --declarationMap --soureMap --inlineSourceMap
  这使得您可以更容易地自定义构建的某些部分,其中可能有不同的开发和生产构建。
  例如,开发版本的库可能不需要生成声明文件,但生产版本需要。项目可以配置声明发射为默认关闭,并简单地进行构建 tsc --build -p ./my-project-dir
  完成内部循环后,"生产"构建只需传递——declaration标志即可。 tsc --build -p ./my-project-dir --declaration
  Exhaustiveswitch/caseCompletions
  当编写switch语句时,TypeScript现在会检测被检查的值何时具有文字类型。如果是这样,它将为每个case提供一个完整的框架。
  速度、内存和包大小的优化
  TypeScript 5.0在我们的代码结构、数据结构和算法实现方面包含了许多强大的更改。这些都意味着你的整个体验应该更快——不仅仅是运行TypeScript,甚至是安装它。
  相对于TypeScript 4.9,我们在速度和大小方面取得了一些有趣的胜利。
  场景
  相对于 TS 4.9时间或者大小
  Material-ui构建时间
  90%
  Playwright 构建时间
  89%
  tsc 启动时间
  89%
  tsc 构建时间
  86%
  Outlook 构建时间
  83%
  VS Code 构建时间
  81%
  typescript 打包大小
  58%
  换句话说,我们发现TypeScript 5.0 Beta版构建VS Code所花费的时间仅为TypeScript 4.9的81%。
  如何?有一些值得注意的改进,我们希望在未来提供更多的细节。但我们不会让你等待那篇博客文章。
  首先,我们最近将TypeScript从命名空间迁移到模块,允许我们利用现代构建工具来执行作用域提升等优化。使用这个工具,重新审视我们的打包策略,并移除一些废弃的代码,已经从TypeScript 4.9的63.8 MB包大小中削减了约26.5 MB。通过直接调用函数,它也显著地提高了速度。
  TypeScript还为编译器中的内部对象类型增加了更多的一致性,同时也精简了某些对象类型。这减少了多态和大态的使用站点,同时抵消了一些作为权衡而来的内存占用。
  我们还在将信息序列化为字符串时执行了一些缓存。类型显示,可能作为错误报告、声明发出、代码完成等的一部分发生,最终可能会非常昂贵。TypeScript现在缓存了一些常用的机制,以便在这些操作中重用。
  总的来说,我们期望大多数代码库从TypeScript 5.0开始速度都将得到提升,并始终能够重现10%到20%的优势。当然,这取决于硬件和代码库的特性,但我们鼓励您今天就在代码库中尝试一下!
  运行时的要求
  TypeScript现在以ECMAScript 2018为目标。对于Node用户来说,这意味着最低版本要求至少是Node.js 10及更高版本。 lib.d.ts 变更
  DOM类型生成方式的改变可能会对现有代码产生影响。值得注意的是,某些属性已经从数值类型转换为数值字面量类型,处理cut、copy和paste事件的属性和方法也在不同接口之间进行了移动。 API重大变化
  在TypeScript 5.0中,我们转向了模块,删除了一些不必要的接口,并对正确性进行了一些改进。有关更改的更多详细信息,请参阅我们的API重大更改页面。 关系运算符中禁止的隐式强制操作
  如果你编写的代码可能导致隐式的字符串到数字转换,TypeScript中的某些操作将会警告你: function func(ns: number | string) {   return ns * 4; // Error, 可能的隐式转换 }
  在5.0中,这也适用于关系操作符>、<、<=和>=: function func(ns: number | string) {   return ns > 4; //错误 }
  如果需要,可以使用+显式强制操作数为数字: function func(ns: number | string) {   return +ns > 4; // OK }
  这种正确性的改进由Mateusz提供Burzyński。 Enum改革
  自TypeScript第一次发布以来,枚举一直存在一些奇怪的问题。在5.0中,我们清理了其中的一些问题,同时减少了理解可以声明的各种枚举所需的概念数量。
  你可能会看到两个主要的新错误。第一,将域外字面量赋值给enum类型现在会出错,正如我们所料: enum SomeEvenDigit {     Zero = 0,     Two = 2,     Four = 4 }  // 错误 let m: SomeEvenDigit = 1;
  另一个问题是,声明某种类型的间接字符串/数字混合枚举形式会错误地创建一个全数字枚举: enum Letters {     A = "a" } enum Numbers {     one = 1,     two = Letters.A }  // Now correctly an error const t: number = Numbers.two;
  在experimentalDecorators下为构造函数中的参数装饰器进行更准确的类型检查
  TypeScript 5.0使得——experimentalDecorators下的装饰器的类型检查更加准确。在对构造函数参数使用装饰器时,这一点变得很明显。 export declare const inject:   (entity: any) =>     (target: object, key: string | symbol, index?: number) => void;  export class Foo {}  export class C {     constructor(@inject(Foo) private x: any) {     } }
  这个调用将失败,因为key需要一个string|symbol,但构造函数参数接收到的键是undefined。正确的修复方法是改变inject中的key类型。如果你使用的库无法升级,一个合理的解决方案是将inject包装在一个更类型安全的装饰器函数中,并对键使用类型断言。
  弃用和默认更改
  在TypeScript 5.0中,我们已经弃用了以下设置和设置值: --target: ES3 --out --noImplicitUseStrict --keyofStringsOnly --suppressExcessPropertyErrors --suppressImplicitAnyIndexErrors --noStrictGenericChecks --charset --importsNotUsedAsValues --preserveValueImports prepend  in project references
  这些配置将继续被允许,直到TypeScript 5.5版本,那时它们将被完全删除,然而,如果你使用这些设置,你将收到一个警告。在TypeScript 5.0以及未来的版本5.1、5.2、5.3和5.4中,你可以指定" ignoreprecations ": "5.0"来消除这些警告。我们还将很快发布一个4.9补丁,允许指定ignoreprecations以允许更顺利的升级。除了弃用之外,我们还更改了一些设置,以更好地改进TypeScript中的跨平台行为。
  ——newLine,用于控制JavaScript文件中的行结尾,如果不指定,则根据当前操作系统推断。我们认为构建应该尽可能确定,Windows记事本现在支持换行行结束,所以新的默认设置是LF。旧的特定于操作系统的推理行为不再可用。
  ——forceConsistentCasingInFileNames,这确保了在一个项目中所有对相同文件名的引用都同意使用大小写,现在默认为true。这可以帮助捕获在不区分大小写的文件系统上编写的代码差异问题。
  您可以留下反馈并查看关于5.0弃用的跟踪问题的更多信息 接下来是什么?
  TypeScript 5.0正在成为一个伟大的版本。在接下来的几周里,我们将专注于bug修复、稳定性和优化即将发布的候选版本,然后是第一个稳定版本。
  和往常一样,关于我们的发行版的细节(包括目标日期!)可以在TypeScript 5.0迭代计划中找到。我们希望迭代计划使TypeScript 5.0更容易围绕您和您的团队的时间表进行测试!
  我们也希望TypeScript 5.0 Beta版能带来很多你期待已久的新功能。让我们的beta版本(或我们的夜间构建)今天尝试一下,让我们知道你的想法!

苹果企业签名的常见问题及解决方法苹果企业签名的基本原理是什么?签名完成以后,该怎么办?以下是常见问题及解决方法。一是苹果企业签名的执行过程中存在歧义。苹果企业签名是通过Macintosh操作系统的Command指MIUI14新功能介绍和获得更新设备列表,看这篇就够!头条创作挑战赛昨天,小米公开了新的MIUI14,带来许多升级与变化,特别是从效率和隐私的角度来看,同时还发布小米13和一些设备,包括WatchS2和迷你PC。虽然最初的更新日志最近推窗见绿出门即景公园城市让市民惊喜不断来源温州广电快点温州推窗见绿出门入园是人们对美好生活的愿景。近年来,温州市综合行政执法局园林绿化管理中心,始终秉持以人民为中心的发展理念,坚持公园主题化建设的同时,深入开展袪疤栽花难以复刻的电影感从何而来?编辑刘珞琦责编杜思梦近些年,电影放映在商业影院外,已经逐渐延伸到学校企业度假酒店户外等更丰富的场景当中。我们在这些场景看到的电影画面,尤其观看同一部电影时,有时觉得在影院的视效品质太阳面临两难境地,要库兹马就留不住约翰逊,是否交易他已有答案头条创作挑战赛据TheAthletic的知名记者沙姆斯查拉尼亚报道,菲尼克斯太阳队有兴趣在2月份的交易截止日期前交易得到奇才队前锋凯尔库兹马。而据报道,奇才队对亚特兰大老鹰队的大前38岁斯嘉丽约翰逊久违现身,曼妙身姿惹眼,替身的性别引猜测当地时间12月5日下午,斯嘉丽约翰逊出现在佐治亚州的萨凡纳市,她正在这里拍摄阿尔忒弥斯计划(ProjectArtemis),一部定于明年上映的爱情喜剧电影。穿着复古款式修身连衣裙的杰伦约翰逊在场5分钟输8分,老鹰想要火箭伊森,美媒6换2方案出炉赢球当然开心了,队友们给绝杀功臣AJ格里芬浇水,后者在加时赛最后时刻接到特雷杨传球空接上篮干掉了猛龙。虽然AJ格里芬的父亲阿德里安格里芬是猛龙的助理教练,但他一定会为自己的儿子感到约翰逊在亚洲参加活动还抹黑中国,主办方彭博社创始人道歉冒犯了(观察者网讯)这一周,G20峰会和APEC领导人会议相继在印尼和泰国举行。世界瞩目亚洲之际,美国亿万富翁彭博社创始人布隆伯格在新加坡举行了一场大型商业活动,汇集了数百名亚洲商人投资CBA最新消息王治郅入选男篮,汤杰二次失业,首钢裁掉约翰逊王治郅入选男篮担任助理教练。北京时间11月17日,中国男篮在昨天通过官方社交媒体,正式官宣更换中国男篮主教练的消息,新任主教练将会由来自塞尔维亚籍的教练员乔尔杰维奇接任,杜锋指导正CBA最新消息!广东引进强援,于澍龙成为教练,首钢裁掉约翰逊杜锋下课之后,广东宏远球迷却是拍手称快,毕竟球队不用继续折腾了。虽然在外援方面还没有最新的进展,但是广东宏远却迎来了一位强援,就是中国男篮的助理教练桑坦德。由于乔尔杰维奇上任,这位惊爆!两届UFC重量级冠军挑战者安东尼约翰逊去世,享年38岁前美国第二大MMA赛事Bellator拳手两届UFC重量级冠军挑战者KO艺术家安东尼约翰逊(AnthonyRumbleJohnson)去世。年仅38岁。MMA界的许多知名人士在社交
2022上半年的11大烂剧,如果你一部都没看,恭喜你成功避雷2022年随着一部开端的火爆,不少人都以为今年将会涌现出非常多的好剧。然而这不过才半年的时候过去了,观众们发现好剧没多少但烂剧却是开始大行其道,其中更是不乏很多大制作大阵容都相继翻美女陪睡333天!吴秀波等一众男星们是怎么毁掉自己的?娱乐圈的瓜真是一波未平,一波又起。曾经耳熟能详的男明星们接二连三地倒下不是被抓进去了,就是被封杀,一个个曾经熟悉的面孔都成为了历史还记得周迅在一次走红毯的活动中喜滋滋地说到好多人啊消失的孩子最好的家庭关系,是爸爸支持妈妈,妈妈支持孩子家,不是一个人的独角戏,而是三个人的舞台剧。爸爸和妈妈合力演绎,家庭才能幸福满满。作者淼淼妈消失的孩子迎来了大结局,在楼道里失踪的杨莫终于被找到了。原来,这不止是场精心设计的绑架,辽宁人喝酒怪象,不喝老村长和凤城老窖,最偏爱的反而是这4款酒东北人是个特殊的群体,三个省份的人被叫一个名字在全中国还是独一份,像是云贵川三省虽然同在祖国西南,却也没有统称为西南人。这是因为黑吉辽三省有着相似的气候环境和地理条件,这造成了东北秋天里有一辆风行游艇是一种什么体验?再也不会错过日落秋高气爽好时节,几乎每天都宜自驾出游,约上三五好友或携亲人,开车驰骋于祖国大好河山,去遇见最美的秋景。那么问题来了,到底该开一辆什么样的车出游,才既能在拍照时凹造型,又能感受长途驾散文秋天的离别秋天,如果是一场生动的遇见,那该多好,偏偏是一场别离。今年的秋天是缓缓来的,而他却是匆匆走的。他是经过七十五载人间秋天的人,是因为他懂得了秋天,才随秋天走的吧。他,本族的兄弟,金声这个秋天最奢侈的十件小事人间最美是清秋她,有碧云天,黄叶地之色彩,她,有来得清来得静来得悲凉之情愫,她,有月正圆,蟹正肥,桂花皎洁之趣味秋天,一切景象全仿佛是诗,说不出的和谐,说不尽的美。今天,与你分享秋世体安切洛蒂现场观看皇马B队比赛,小将阿里巴斯表现抢眼直播吧9月26日讯据西班牙世界体育报报道,皇马主帅安切洛蒂上周日赴现场观看了皇马B队的比赛,他考察了B队的球员们。安切洛蒂是皇马B队比赛的现场常客,他非常关注小将们的成长,上周日皇长期熬夜危害到底有多大?该如何改善睡眠?在近几年来,大家由于工作学习手机的原因,小编相信很多人已经形成了长期熬夜的习惯。熬夜的危害若长期熬夜可能会造成第二天工作学习效率低下,精神方面会变得更加易怒,焦躁,健忘。免疫力也会加州大学研究适当饮酒比运动更易长寿,每天两杯酒养生靠谱吗?适度喝酒能延寿,比天天运动强多了!周先生是个酒鬼,每天晚饭雷打不动干上二两酒,别人怎么劝他戒酒,都是左耳进右耳出。前一段时间,他在网上看到一篇文章,里面说每天适度喝酒延长寿命的效果宋亚东将进行恐怖手术摘出眼球,植入钛合金近日,宋亚东在和刘平原连麦时透露了自己的伤情,原来比赛中宋亚东不仅左边眉弓被打开,而且右眼也因眼眶骨折出现了重影,几乎是在和对手盲打。而且他还表示自己将进行右眼的手术需要将眼球取出