github evanw/esbuild v0.17.5

latest releases: v0.24.0, v0.23.1, v0.23.0...
21 months ago
  • Parse const type parameters from TypeScript 5.0

    The TypeScript 5.0 beta announcement adds const type parameters to the language. You can now add the const modifier on a type parameter of a function, method, or class like this:

    type HasNames = { names: readonly string[] };
    const getNamesExactly = <const T extends HasNames>(arg: T): T["names"] => arg.names;
    const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });

    The type of names in the above example is readonly ["Alice", "Bob", "Eve"]. Marking the type parameter as const behaves as if you had written as const at every use instead. The above code is equivalent to the following TypeScript, which was the only option before TypeScript 5.0:

    type HasNames = { names: readonly string[] };
    const getNamesExactly = <T extends HasNames>(arg: T): T["names"] => arg.names;
    const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] } as const);

    You can read the announcement for more information.

  • Make parsing generic async arrow functions more strict in .tsx files

    Previously esbuild's TypeScript parser incorrectly accepted the following code as valid:

    let fn = async <T> () => {};

    The official TypeScript parser rejects this code because it thinks it's the identifier async followed by a JSX element starting with <T>. So with this release, esbuild will now reject this syntax in .tsx files too. You'll now have to add a comma after the type parameter to get generic arrow functions like this to parse in .tsx files:

    let fn = async <T,> () => {};
  • Allow the in and out type parameter modifiers on class expressions

    TypeScript 4.7 added the in and out modifiers on the type parameters of classes, interfaces, and type aliases. However, while TypeScript supported them on both class expressions and class statements, previously esbuild only supported them on class statements due to an oversight. This release now allows these modifiers on class expressions too:

    declare let Foo: any;
    Foo = class <in T> { };
    Foo = class <out T> { };
  • Update enum constant folding for TypeScript 5.0

    TypeScript 5.0 contains an updated definition of what it considers a constant expression:

    An expression is considered a constant expression if it is

    • a number or string literal,
    • a unary +, -, or ~ applied to a numeric constant expression,
    • a binary +, -, *, /, %, **, <<, >>, >>>, |, &, ^ applied to two numeric constant expressions,
    • a binary + applied to two constant expressions whereof at least one is a string,
    • a template expression where each substitution expression is a constant expression,
    • a parenthesized constant expression,
    • a dotted name (e.g. x.y.z) that references a const variable with a constant expression initializer and no type annotation,
    • a dotted name that references an enum member with an enum literal type, or
    • a dotted name indexed by a string literal (e.g. x.y["z"]) that references an enum member with an enum literal type.

    This impacts esbuild's implementation of TypeScript's const enum feature. With this release, esbuild will now attempt to follow these new rules. For example, you can now initialize an enum member with a template literal expression that contains a numeric constant:

    // Original input
    const enum Example {
      COUNT = 100,
      ERROR = `Expected ${COUNT} items`,
    }
    console.log(
      Example.COUNT,
      Example.ERROR,
    )
    
    // Old output (with --tree-shaking=true)
    var Example = /* @__PURE__ */ ((Example2) => {
      Example2[Example2["COUNT"] = 100] = "COUNT";
      Example2[Example2["ERROR"] = `Expected ${100 /* COUNT */} items`] = "ERROR";
      return Example2;
    })(Example || {});
    console.log(
      100 /* COUNT */,
      Example.ERROR
    );
    
    // New output (with --tree-shaking=true)
    console.log(
      100 /* COUNT */,
      "Expected 100 items" /* ERROR */
    );

    These rules are not followed exactly due to esbuild's limitations. The rule about dotted references to const variables is not followed both because esbuild's enum processing is done in an isolated module setting and because doing so would potentially require esbuild to use a type system, which it doesn't have. For example:

    // The TypeScript compiler inlines this but esbuild doesn't:
    declare const x = 'foo'
    const enum Foo { X = x }
    console.log(Foo.X)

    Also, the rule that requires converting numbers to a string currently only followed for 32-bit signed integers and non-finite numbers. This is done to avoid accidentally introducing a bug if esbuild's number-to-string operation doesn't exactly match the behavior of a real JavaScript VM. Currently esbuild's number-to-string constant folding is conservative for safety.

  • Forbid definite assignment assertion operators on class methods

    In TypeScript, class methods can use the ? optional property operator but not the ! definite assignment assertion operator (while class fields can use both):

    class Foo {
      // These are valid TypeScript
      a?
      b!
      x?() {}
    
      // This is invalid TypeScript
      y!() {}
    }

    Previously esbuild incorrectly allowed the definite assignment assertion operator with class methods. This will no longer be allowed starting with this release.

Don't miss a new esbuild release

NewReleases is sending notifications on new releases.