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

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版本(或我们的夜间构建)今天尝试一下,让我们知道你的想法!

穷山恶水出刁民?甘孜刘局长精心打造的知名度,被三根绳勒回去了前言近两年,在甘孜文旅局局长刘洪的精心设计下,甘孜州的知名度有很大的提升。甘孜州也成为旅游的一块名片了。刘局长利用自己的长相优势,再配合当下火热的短视频,迅速成为了甘孜州的代言人。别装了,名媛们,那不是高贵,你的气质出卖了你前不久,网友爆出自己花500大洋进了个名媛群话说这所谓的名媛群自诩可以可以结交名媛,认识金融大鳄。好奇问一下,这网友是不是也有这想法?莫生气,只是好奇好奇一下而已,莫当真,简单点说萌娃对话大自然植此青绿共享成长孩子们在种植活动中体验到劳动的乐趣。春意满园宣传展板。红网时刻新闻3月11日讯(记者刘惟烜通讯员刘琴)3月11日,植树节前夕,在添一点绿色多一份美好的季节里,长沙市天心区教育局新兴当代大学生就业现状有困难,但很多人闯出新路子随着春招的来临应届毕业生们面临着步入社会就业创业的难题而有些大学生则把就业玩出了新花样下面就跟随团团一起来看看大学生们的花样就业吧到祖国最需要的地方去到西部去,到基层去到祖国最需要马忠明小种子夯实粮食安全大根基工作中的马忠明(左)(受访者供图)央视网消息(记者王小英)作为一名农业科研工作者,全国政协委员马忠明一年中有超过三分之一的时间都是在田间地头度过的。他关心土壤,关心种子,关心粮食安借身体迁升的三位美女官员落马云南省投资促进局原党组书记局长段颖官宣落马,这是全国首个落马的80后正厅级女干部。从调查结果可以看出在多年以来她曾与多名异性维持不正当关系。而她的快速晋升之路也是得益于此,其背后更投行内控12大问题!监管逐一剖析,执业重程序达标轻实质判断,过会导向突出点蓝字关注,不迷路来源券商中国随着全面注册制的正式实施,以信息披露为核心的审核理念,对券商合规风控能力提出更高要求。证券时报券商中国记者从投行人士处获悉,近日,深圳证监局在内部对券宋春丽夫妻相爱,61岁才做母亲在这个看脸的时代,颜值成了进入演艺圈的一个敲门砖,而且这个颜值高低为明星星途璀璨开通了大门。谢娜身为电视台一姐的主持人,凭借疯癫的风格成为了许多年轻观众的开心果,从主持电视台娱乐节想摊牌?秦刚警告话音刚落,台当局叫嚣从来不是中国的一部分3月7日,我外交部长秦刚在外长记者会上就涉及台湾问题的话题发表讲话。他手持提前准备好的宪法,同时强调台湾是中国的一部分,任何人都不能低估中国政府和中国人民捍卫国家主权和领土完整的决葱省大网红市城投信托产品逾期后续葱省大网红市某县政信信托产品产品超过T10个工作日仍然未兑付,该信托产品的投资人也是焦麻了不过,据不可靠消息,下周二之前大概率可以兑付,拭目以待。希望是虚惊一场,毕竟政信信仰城投信乌克兰获得战机支援,俄罗斯任重道远,需速战速决或中国从中调停当地时间3月9日,波兰广播电台报道称,目前斯洛伐克国防部长雅罗斯拉夫纳吉在社交网站上称,波兰同意与斯洛伐克共同向乌克兰提供米格29战斗机。双方放在在3月8日的斯德哥尔摩的欧盟防长非
在四个直辖市中,重庆房价最低,你到重庆买房吗?一重庆不宜居。1火炉城市,夏天长,闷热难受2山城,爬坡上坎,汗流夹背,出行成本高3重庆日常消费物价高过邻居成都,居民人均收入全国二线大城市下游水平。重庆历年最低保障工资和每年退休职用奶瓶喂养时应注意哪些细节?对于需要采取人工喂养的宝贝,妈咪们最头痛的就是宝贝一次喂奶的量该如何掌握?喂奶的时间间隔?配方奶粉应如何调配?是否需要喝水等问题。一次喂奶量的掌握掌握一次喂奶量的方法,最常用的有两小时候谁干过离家出走的事?我十六时离家出走一次,是大年初一,早上在四爷爷家吃的饭,吃完饭就走了。原因是年三十继父不让我吃饭,我就和他打起来了,母亲也来打我让我踹了一脚好像挺重当时母倒地上了,我把饭桌也都掀翻跳舞最大的益处是什么?现在,人们喜欢看别人跳舞。然而,你可能没有意识到的是,如果你从沙发上起来自己跳舞,这是保持身心健康的好方法。研究表明跳舞可以帮助你减肥重量保持灵活,减轻压力,交朋友,等等。你还在等我75岁了,儿子催着叫我把房产证的名字改成他的,可以吗?不可以,世事难料,不到最后一天都不要这么做,主动权要掌握在自己手里。感谢邀请作为一名房产专业人士来说,房产产权转移事关重大,建议谨慎行事。现在题主已经75岁了,老伴已经去世了,而且初三数学满分150考了78,经济条件可以,但是家长就是不掏钱报辅导班,这是什么心态?孩子数学满分150分,考了78分,经济条件可以,家长就是不掏钱补课报辅导班,我认为家长不是扣,是一种理智的表现,我赞同。一,家长了解孩子的实际情况,知道怎么补课都无济于事了,不但浪幼儿园教宝宝英语合适吗?真的可以提前适应英语环境吗,你怎么看?可以学一些基础的单词妈妈,幼儿园教宝宝英语是合适的,是真的可以提前让宝宝适应英语环境的。妈妈之所以有这种担忧,是因为我们在给宝宝做决定的时候,往往会进入以下2个误区(1)我觉得孩子汽车胎噪和风噪的问题,该如何解决?我开了10几年车,也做过二手车行业,各种品牌的车都开过不少,便宜到只值几千元的老旧二手车,贵到崭新的宝马7系,我都开过。对于怎么解决风噪声和胎噪声,我有一些自己的见解。首先说胎噪胎王楚钦,徐英彬,分别3比1胜马龙,樊振东,会成为领军人物吗?昨天我看了这两场比赛,被两位小将的精气神给征服了,他们临场不乱,打出了自己的风格,从表现来看,是值得我们以后对他们有所期待的。昨天王楚钦3比1战胜马龙,比赛的状态来看非常好,就是低大连农村有好多农家小院出售,价格便宜,如果买来养老,需要注意哪些问题?私下民房交易应该受到保护!我在2006年10月通过当地战友推荐,与当地某村民达成协议,购买一处已遗弃不用的民房,并在当地镇政府做了司法见证。当时购房动机就是愿了未了的旧情,即少年随新时代来临,大量失业人口,那些没文化,没技术的人该如何生存?没有文化,没有技术,至少目前还有很多地方可以就业的1可以去送外卖呀!只需要一部手机,一部电动车,一个健康证,都可以正常上岗了!时间自由,上班灵活,勤快一点月薪过万不是梦。还会担心生